summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.hound.yml4
-rw-r--r--.teatro.yml8
-rw-r--r--CHANGELOG30
-rw-r--r--CONTRIBUTING.md3
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock62
-rw-r--r--app/assets/javascripts/api.js.coffee2
-rw-r--r--app/assets/javascripts/compare_autocomplete.js.coffee41
-rw-r--r--app/assets/javascripts/dispatcher.js.coffee4
-rw-r--r--app/assets/javascripts/gl_dropdown.js.coffee23
-rw-r--r--app/assets/javascripts/issuable.js.coffee4
-rw-r--r--app/assets/javascripts/labels_select.js.coffee31
-rw-r--r--app/assets/javascripts/milestone_select.js.coffee2
-rw-r--r--app/assets/javascripts/projects_list.js.coffee11
-rw-r--r--app/assets/javascripts/shortcuts.js.coffee13
-rw-r--r--app/assets/javascripts/users_select.js.coffee9
-rw-r--r--app/assets/stylesheets/framework/avatar.scss1
-rw-r--r--app/assets/stylesheets/framework/blank.scss11
-rw-r--r--app/assets/stylesheets/framework/common.scss15
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss2
-rw-r--r--app/assets/stylesheets/framework/gitlab-theme.scss1
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss61
-rw-r--r--app/assets/stylesheets/framework/variables.scss2
-rw-r--r--app/assets/stylesheets/pages/builds.scss8
-rw-r--r--app/assets/stylesheets/pages/labels.scss8
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss11
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss158
-rw-r--r--app/assets/stylesheets/pages/projects.scss6
-rw-r--r--app/assets/stylesheets/pages/status.scss12
-rw-r--r--app/assets/stylesheets/pages/tree.scss3
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb6
-rw-r--r--app/finders/todos_finder.rb2
-rw-r--r--app/helpers/appearances_helper.rb2
-rw-r--r--app/helpers/ci_status_helper.rb4
-rw-r--r--app/helpers/issuables_helper.rb4
-rw-r--r--app/helpers/projects_helper.rb7
-rw-r--r--app/helpers/todos_helper.rb1
-rw-r--r--app/models/label.rb9
-rw-r--r--app/models/project.rb10
-rw-r--r--app/models/todo.rb12
-rw-r--r--app/models/user.rb2
-rw-r--r--app/services/projects/import_service.rb2
-rw-r--r--app/views/admin/builds/_build.html.haml65
-rw-r--r--app/views/admin/builds/index.html.haml10
-rw-r--r--app/views/dashboard/projects/_zero_authorized_projects.html.haml87
-rw-r--r--app/views/dashboard/projects/index.html.haml3
-rw-r--r--app/views/dashboard/todos/index.html.haml4
-rw-r--r--app/views/help/_shortcuts.html.haml4
-rw-r--r--app/views/layouts/_collapse_button.html.haml3
-rw-r--r--app/views/layouts/_page.html.haml12
-rw-r--r--app/views/layouts/header/_default.html.haml18
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml20
-rw-r--r--app/views/projects/builds/index.html.haml8
-rw-r--r--app/views/projects/ci/builds/_build.html.haml78
-rw-r--r--app/views/projects/ci/pipelines/_pipeline.html.haml64
-rw-r--r--app/views/projects/commit/_pipeline.html.haml4
-rw-r--r--app/views/projects/compare/_form.html.haml18
-rw-r--r--app/views/projects/compare/_ref_dropdown.html.haml4
-rw-r--r--app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml2
-rw-r--r--app/views/projects/issues/index.html.haml2
-rw-r--r--app/views/projects/new.html.haml7
-rw-r--r--app/views/projects/pipelines/index.html.haml6
-rw-r--r--app/views/projects/snippets/index.html.haml2
-rw-r--r--app/views/shared/_import_form.html.haml2
-rw-r--r--app/views/shared/icons/_group.svg.erb (renamed from app/views/shared/icons/_group.svg)9
-rw-r--r--app/views/shared/icons/_icon_commit.svg3
-rw-r--r--app/views/shared/icons/_icon_timer.svg1
-rw-r--r--app/views/shared/icons/_project.svg10
-rw-r--r--app/views/shared/icons/_project.svg.erb3
-rw-r--r--app/views/shared/issuable/_filter.html.haml4
-rw-r--r--app/views/shared/issuable/_form.html.haml36
-rw-r--r--app/views/shared/issuable/_label_dropdown.html.haml14
-rw-r--r--app/views/shared/issuable/_milestone_dropdown.html.haml8
-rw-r--r--config/gitlab.teatro.yml87
-rw-r--r--db/migrate/20160712171823_remove_award_emojis_with_no_user.rb21
-rw-r--r--db/schema.rb2
-rw-r--r--doc/api/todos.md2
-rw-r--r--doc/development/ui_guide.md48
-rw-r--r--doc/integration/oauth_provider.md3
-rw-r--r--features/project/issues/issues.feature1
-rw-r--r--features/steps/project/forked_merge_requests.rb12
-rw-r--r--features/steps/project/issues/issues.rb3
-rw-r--r--features/support/env.rb2
-rw-r--r--lib/api/entities.rb1
-rw-r--r--lib/api/projects.rb6
-rw-r--r--lib/banzai/renderer.rb14
-rw-r--r--lib/gitlab/diff/inline_diff.rb52
-rw-r--r--lib/gitlab/diff/parallel_diff.rb129
-rw-r--r--lib/gitlab/gitlab_import/importer.rb1
-rw-r--r--lib/support/nginx/gitlab7
-rw-r--r--lib/support/nginx/gitlab-ssl7
-rw-r--r--spec/factories/todos.rb4
-rw-r--r--spec/features/compare_spec.rb42
-rw-r--r--spec/features/issues/move_spec.rb2
-rw-r--r--spec/features/issues_spec.rb5
-rw-r--r--spec/features/login_spec.rb43
-rw-r--r--spec/finders/notes_finder_spec.rb2
-rw-r--r--spec/fixtures/parallel_diff_result.yml34
-rw-r--r--spec/javascripts/project_title_spec.js.coffee2
-rw-r--r--spec/lib/banzai/filter/label_reference_filter_spec.rb110
-rw-r--r--spec/lib/banzai/object_renderer_spec.rb11
-rw-r--r--spec/lib/ci/gitlab_ci_yaml_processor_spec.rb4
-rw-r--r--spec/lib/gitlab/build_data_builder_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/file_spec.rb4
-rw-r--r--spec/lib/gitlab/diff/inline_diff_spec.rb28
-rw-r--r--spec/lib/gitlab/diff/parser_spec.rb2
-rw-r--r--spec/lib/gitlab/gitlab_import/importer_spec.rb53
-rw-r--r--spec/lib/gitlab/ldap/access_spec.rb2
-rw-r--r--spec/lib/gitlab/ldap/user_spec.rb2
-rw-r--r--spec/models/ci/pipeline_spec.rb10
-rw-r--r--spec/models/ci/variable_spec.rb2
-rw-r--r--spec/models/commit_status_spec.rb14
-rw-r--r--spec/models/concerns/mentionable_spec.rb2
-rw-r--r--spec/models/forked_project_link_spec.rb2
-rw-r--r--spec/models/generic_commit_status_spec.rb10
-rw-r--r--spec/models/global_milestone_spec.rb6
-rw-r--r--spec/models/group_spec.rb10
-rw-r--r--spec/models/members/project_member_spec.rb4
-rw-r--r--spec/models/milestone_spec.rb10
-rw-r--r--spec/models/namespace_spec.rb8
-rw-r--r--spec/models/project_security_spec.rb2
-rw-r--r--spec/models/project_services/buildkite_service_spec.rb6
-rw-r--r--spec/models/project_spec.rb22
-rw-r--r--spec/models/repository_spec.rb20
-rw-r--r--spec/models/service_spec.rb6
-rw-r--r--spec/models/user_spec.rb12
-rw-r--r--spec/requests/api/projects_spec.rb12
-rw-r--r--spec/services/ci/create_trigger_request_service_spec.rb2
-rw-r--r--spec/services/ci/image_for_build_service_spec.rb2
-rw-r--r--spec/services/ci/register_build_service_spec.rb2
-rw-r--r--spec/services/create_commit_builds_service_spec.rb2
-rw-r--r--spec/services/event_create_service_spec.rb20
-rw-r--r--spec/services/issues/close_service_spec.rb2
-rw-r--r--spec/services/merge_requests/close_service_spec.rb2
-rw-r--r--spec/services/merge_requests/create_service_spec.rb2
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb2
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb2
-rw-r--r--spec/services/merge_requests/reopen_service_spec.rb2
-rw-r--r--spec/services/milestones/close_service_spec.rb2
-rw-r--r--spec/services/milestones/create_service_spec.rb2
-rw-r--r--spec/services/notes/create_service_spec.rb2
-rw-r--r--spec/services/notes/post_process_service_spec.rb2
-rw-r--r--spec/services/notification_service_spec.rb4
-rw-r--r--spec/services/test_hook_service_spec.rb2
-rw-r--r--spec/support/login_helpers.rb34
-rw-r--r--spec/support/omni_auth.rb1
146 files changed, 1303 insertions, 814 deletions
diff --git a/.hound.yml b/.hound.yml
deleted file mode 100644
index 3bde29fb2bf..00000000000
--- a/.hound.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-# Prefer single quotes
-StringLiterals:
- EnforcedStyle: single_quotes
- Enabled: true
diff --git a/.teatro.yml b/.teatro.yml
deleted file mode 100644
index 30054361981..00000000000
--- a/.teatro.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-stage:
- before:
- - cp config/gitlab.teatro.yml config/gitlab.yml
- - mkdir /apps/gitlab-satellites
- - mkdir /apps/repositories
-
- database:
- - RAILS_ENV=development force=yes bundle exec rake db:create gitlab:setup \ No newline at end of file
diff --git a/CHANGELOG b/CHANGELOG
index fa6044bd87c..51f1db07e8b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,19 +2,23 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.10.0 (unreleased)
- Expose {should,force}_remove_source_branch (Ben Boeckel)
+ - Fix projects dropdown loading performance with a simplified api cal. !5113 (tiagonbotelho)
- Fix commit builds API, return all builds for all pipelines for given commit. !4849
- Replace Haml with Hamlit to make view rendering faster. !3666
- Refresh the branch cache after `git gc` runs
- Refactor repository paths handling to allow multiple git mount points
- Optimize system note visibility checking by memoizing the visible reference count !5070
- Add Application Setting to configure default Repository Path for new projects
+ - Delete award emoji when deleting a user
- Remove pinTo from Flash and make inline flash messages look nicer !4854 (winniehell)
- Wrap code blocks on Activies and Todos page. !4783 (winniehell)
- Align flash messages with left side of page content !4959 (winniehell)
- Display tooltip for "Copy to Clipboard" button !5164 (winniehell)
+ - Use default cursor for table header of project files !5165 (winniehell)
- Display last commit of deleted branch in push events !4699 (winniehell)
- Escape file extension when parsing search results !5141 (winniehell)
- Apply the trusted_proxies config to the rack request object for use with rack_attack
+ - Upgrade to Rails 4.2.7. !5236
- Add Sidekiq queue duration to transaction metrics.
- Add a new column `artifacts_size` to table `ci_builds` !4964
- Let Workhorse serve format-patch diffs
@@ -26,6 +30,8 @@ v 8.10.0 (unreleased)
- Add Spring EmojiOne updates.
- Add syntax for multiline blockquote using `>>>` fence !3954
- Fix viewing notification settings when a project is pending deletion
+ - Updated compare dropdown menus to use GL dropdown
+ - Eager load award emoji on notes
- Fix pagination when sorting by columns with lots of ties (like priority)
- The Markdown reference parsers now re-use query results to prevent running the same queries multiple times !5020
- Updated project header design
@@ -34,6 +40,7 @@ v 8.10.0 (unreleased)
- Fix changing issue state columns in milestone view
- Update health_check gem to version 2.1.0
- Add notification settings dropdown for groups
+ - Render inline diffs for multiple changed lines following eachother
- Wildcards for protected branches. !4665
- Allow importing from Github using Personal Access Tokens. (Eric K Idema)
- API: Todos !3188 (Robert Schilling)
@@ -44,6 +51,7 @@ v 8.10.0 (unreleased)
- Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
- Only show New Snippet button to users that can create snippets.
- PipelinesFinder uses git cache data
+ - Actually render old and new sections of parallel diff next to each other
- Throttle the update of `project.pushes_since_gc` to 1 minute.
- Allow expanding and collapsing files in diff view (!4990)
- Collapse large diffs by default (!4990)
@@ -73,7 +81,6 @@ v 8.10.0 (unreleased)
- Allow '?', or '&' for label names
- Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests
- Add date when user joined the team on the member page
- - Fix 404 redirect after validation fails importing a GitLab project
- Fix 404 redirect after validation fails importing a GitLab project
- Added setting to set new users by default as external !4545 (Dravere)
- Add min value for project limit field on user's form !3622 (jastkand)
@@ -83,8 +90,22 @@ v 8.10.0 (unreleased)
- Style of import project buttons were fixed in the new project page. !5183 (rdemirbay)
- Fix GitHub client requests when rate limit is disabled
- Optimistic locking for Issues and Merge Requests (Title and description overriding prevention)
-
-v 8.9.6 (unreleased)
+ - Redesign Builds and Pipelines pages
+ - Change status color and icon for running builds
+ - Fix markdown rendering for: consecutive labels references, label references that begin with a digit or contains `.`
+
+v 8.9.6
+ - Fix importing of events under notes for GitLab projects. !5154
+ - Fix log statements in import/export. !5129
+ - Fix commit avatar alignment in compare view. !5128
+ - Fix broken migration in MySQL. !5005
+ - Overwrite Host and X-Forwarded-Host headers in NGINX !5213
+ - Keeps issue number when importing from Gitlab.com
+
+v 8.9.7 (unreleased)
+ - Fix import_data wrongly saved as a result of an invalid import_url
+
+v 8.9.6
- Fix importing of events under notes for GitLab projects
v 8.9.5
@@ -246,6 +267,7 @@ v 8.9.0
- Add rake task 'gitlab:db:configure' for conditionally seeding or migrating the database
- Changed the Slack build message to use the singular duration if necessary (Aran Koning)
- Fix race condition on merge when build succeeds
+ - Added shortcut to focus filter search fields and added documentation #18120
- Links from a wiki page to other wiki pages should be rewritten as expected
- Add option to project to only allow merge requests to be merged if the build succeeds (Rui Santos)
- Added navigation shortcuts to the project pipelines, milestones, builds and forks page. !4393
@@ -2207,8 +2229,6 @@ v 7.7.0
- Fixes for edit comments: drag-n-drop images, preview mode, selecting images, save & update
- Remove password strength indicator
-
-
v 7.6.0
- Fork repository to groups
- New rugged version
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f4472214778..14ff05c9aa3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -145,7 +145,8 @@ might be edited to make them small and simple.
You are encouraged to use the template below for feature proposals.
```
-## Description including problem, use cases, benefits, and/or goals
+## Description
+Include problem, use cases, benefits, and/or goals
## Proposal
diff --git a/Gemfile b/Gemfile
index 5c43015e52c..ee23f712f05 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,6 @@
source 'https://rubygems.org'
-gem 'rails', '4.2.6'
+gem 'rails', '4.2.7'
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Responders respond_to and respond_with
diff --git a/Gemfile.lock b/Gemfile.lock
index f8018e58a5e..67c0645c3d9 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -3,34 +3,34 @@ GEM
specs:
RedCloth (4.3.2)
ace-rails-ap (4.0.2)
- actionmailer (4.2.6)
- actionpack (= 4.2.6)
- actionview (= 4.2.6)
- activejob (= 4.2.6)
+ actionmailer (4.2.7)
+ actionpack (= 4.2.7)
+ actionview (= 4.2.7)
+ activejob (= 4.2.7)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5)
- actionpack (4.2.6)
- actionview (= 4.2.6)
- activesupport (= 4.2.6)
+ actionpack (4.2.7)
+ actionview (= 4.2.7)
+ activesupport (= 4.2.7)
rack (~> 1.6)
rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
- actionview (4.2.6)
- activesupport (= 4.2.6)
+ actionview (4.2.7)
+ activesupport (= 4.2.7)
builder (~> 3.1)
erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
- activejob (4.2.6)
- activesupport (= 4.2.6)
+ activejob (4.2.7)
+ activesupport (= 4.2.7)
globalid (>= 0.3.0)
- activemodel (4.2.6)
- activesupport (= 4.2.6)
+ activemodel (4.2.7)
+ activesupport (= 4.2.7)
builder (~> 3.1)
- activerecord (4.2.6)
- activemodel (= 4.2.6)
- activesupport (= 4.2.6)
+ activerecord (4.2.7)
+ activemodel (= 4.2.7)
+ activesupport (= 4.2.7)
arel (~> 6.0)
activerecord-session_store (1.0.0)
actionpack (>= 4.0, < 5.1)
@@ -38,7 +38,7 @@ GEM
multi_json (~> 1.11, >= 1.11.2)
rack (>= 1.5.2, < 3)
railties (>= 4.0, < 5.1)
- activesupport (4.2.6)
+ activesupport (4.2.7)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
@@ -515,16 +515,16 @@ GEM
rack
rack-test (0.6.3)
rack (>= 1.0)
- rails (4.2.6)
- actionmailer (= 4.2.6)
- actionpack (= 4.2.6)
- actionview (= 4.2.6)
- activejob (= 4.2.6)
- activemodel (= 4.2.6)
- activerecord (= 4.2.6)
- activesupport (= 4.2.6)
+ rails (4.2.7)
+ actionmailer (= 4.2.7)
+ actionpack (= 4.2.7)
+ actionview (= 4.2.7)
+ activejob (= 4.2.7)
+ activemodel (= 4.2.7)
+ activerecord (= 4.2.7)
+ activesupport (= 4.2.7)
bundler (>= 1.3.0, < 2.0)
- railties (= 4.2.6)
+ railties (= 4.2.7)
sprockets-rails
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
@@ -534,9 +534,9 @@ GEM
rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.3)
loofah (~> 2.0)
- railties (4.2.6)
- actionpack (= 4.2.6)
- activesupport (= 4.2.6)
+ railties (4.2.7)
+ actionpack (= 4.2.7)
+ activesupport (= 4.2.7)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (2.1.0)
@@ -697,7 +697,7 @@ GEM
spring (>= 0.9.1)
spring-commands-teaspoon (0.0.2)
spring (>= 0.9.1)
- sprockets (3.6.2)
+ sprockets (3.6.3)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.1.1)
@@ -920,7 +920,7 @@ DEPENDENCIES
rack-attack (~> 4.3.1)
rack-cors (~> 0.4.0)
rack-oauth2 (~> 1.2.1)
- rails (= 4.2.6)
+ rails (= 4.2.7)
rails-deprecated_sanitizer (~> 1.0.3)
rainbow (~> 2.1.0)
rblineprof (~> 0.3.6)
diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee
index cf46f15a156..89b0ac697ed 100644
--- a/app/assets/javascripts/api.js.coffee
+++ b/app/assets/javascripts/api.js.coffee
@@ -3,7 +3,7 @@
groupPath: "/api/:version/groups/:id.json"
namespacesPath: "/api/:version/namespaces.json"
groupProjectsPath: "/api/:version/groups/:id/projects.json"
- projectsPath: "/api/:version/projects.json"
+ projectsPath: "/api/:version/projects.json?simple=true"
labelsPath: "/api/:version/projects/:id/labels"
licensePath: "/api/:version/licenses/:key"
gitignorePath: "/api/:version/gitignores/:key"
diff --git a/app/assets/javascripts/compare_autocomplete.js.coffee b/app/assets/javascripts/compare_autocomplete.js.coffee
new file mode 100644
index 00000000000..7ad9fd97637
--- /dev/null
+++ b/app/assets/javascripts/compare_autocomplete.js.coffee
@@ -0,0 +1,41 @@
+class @CompareAutocomplete
+ constructor: ->
+ @initDropdown()
+
+ initDropdown: ->
+ $('.js-compare-dropdown').each ->
+ $dropdown = $(@)
+ selected = $dropdown.data('selected')
+
+ $dropdown.glDropdown(
+ data: (term, callback) ->
+ $.ajax(
+ url: $dropdown.data('refs-url')
+ data:
+ ref: $dropdown.data('ref')
+ ).done (refs) ->
+ callback(refs)
+ selectable: true
+ filterable: true
+ filterByText: true
+ fieldName: $dropdown.attr('name')
+ filterInput: 'input[type="text"]'
+ renderRow: (ref) ->
+ if ref.header?
+ $('<li />')
+ .addClass('dropdown-header')
+ .text(ref.header)
+ else
+ link = $('<a />')
+ .attr('href', '#')
+ .addClass(if ref is selected then 'is-active' else '')
+ .text(ref)
+ .attr('data-ref', escape(ref))
+
+ $('<li />')
+ .append(link)
+ id: (obj, $el) ->
+ $el.attr('data-ref')
+ toggleLabel: (obj, $el) ->
+ $el.text().trim()
+ )
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index 74fd77cf7ab..b5da15e9e49 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -39,6 +39,8 @@ class Dispatcher
shortcut_handler = new ShortcutsNavigation()
new GLForm($('.issue-form'))
new IssuableForm($('.issue-form'))
+ new LabelsSelect()
+ new MilestoneSelect()
when 'projects:merge_requests:new', 'projects:merge_requests:edit'
new Diff()
shortcut_handler = new ShortcutsNavigation()
@@ -137,6 +139,8 @@ class Dispatcher
new Project()
new ProjectAvatar()
switch path[1]
+ when 'compare'
+ new CompareAutocomplete()
when 'edit'
shortcut_handler = new ShortcutsNavigation()
new ProjectNew()
diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee
index 1c65e833d47..1b0d0db8954 100644
--- a/app/assets/javascripts/gl_dropdown.js.coffee
+++ b/app/assets/javascripts/gl_dropdown.js.coffee
@@ -456,6 +456,8 @@ class GitLabDropdown
rowClicked: (el) ->
fieldName = @options.fieldName
+ isInput = $(@el).is('input')
+
if @renderedData
groupName = el.data('group')
if groupName
@@ -466,10 +468,19 @@ class GitLabDropdown
selectedObject = @renderedData[selectedIndex]
value = if @options.id then @options.id(selectedObject, el) else selectedObject.id
- field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']")
+
+ if isInput
+ field = $(@el)
+ else
+ field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']")
+
if el.hasClass(ACTIVE_CLASS)
el.removeClass(ACTIVE_CLASS)
- field.remove()
+
+ if isInput
+ field.val('')
+ else
+ field.remove()
# Toggle the dropdown label
if @options.toggleLabel
@@ -490,7 +501,9 @@ class GitLabDropdown
else
if not @options.multiSelect or el.hasClass('dropdown-clear-active')
@dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS
- @dropdown.parent().find("input[name='#{fieldName}']").remove()
+
+ unless isInput
+ @dropdown.parent().find("input[name='#{fieldName}']").remove()
if !value?
field.remove()
@@ -505,7 +518,9 @@ class GitLabDropdown
if !field.length and fieldName
@addInput(fieldName, value)
else
- field.val value
+ field
+ .val value
+ .trigger 'change'
return selectedObject
diff --git a/app/assets/javascripts/issuable.js.coffee b/app/assets/javascripts/issuable.js.coffee
index c71d4ecf505..7f795f8096b 100644
--- a/app/assets/javascripts/issuable.js.coffee
+++ b/app/assets/javascripts/issuable.js.coffee
@@ -32,13 +32,11 @@ issuable_created = false
$search = $('#issue_search')
$form = $('.js-filter-form')
$input = $("input[name='#{$search.attr('name')}']", $form)
-
if $input.length is 0
$form.append "<input type='hidden' name='#{$search.attr('name')}' value='#{_.escape($search.val())}'/>"
else
$input.val $search.val()
-
- Issuable.filterResults $form
+ Issuable.filterResults $form if $search.val() isnt ''
, 500)
initLabelFilterRemove: ->
diff --git a/app/assets/javascripts/labels_select.js.coffee b/app/assets/javascripts/labels_select.js.coffee
index 7688609b301..1a802b81452 100644
--- a/app/assets/javascripts/labels_select.js.coffee
+++ b/app/assets/javascripts/labels_select.js.coffee
@@ -184,20 +184,22 @@ class @LabelsSelect
.value()
if $dropdown.hasClass 'js-extra-options'
- if showNo
- data.unshift(
- id: 0
- title: 'No Label'
- )
-
+ extraData = []
if showAny
- data.unshift(
+ extraData.push(
isAny: true
title: 'Any Label'
)
- if data.length > 2
- data.splice 2, 0, 'divider'
+ if showNo
+ extraData.push(
+ id: 0
+ title: 'No Label'
+ )
+
+ if extraData.length
+ extraData.push 'divider'
+ data = extraData.concat(data)
callback data
@@ -287,6 +289,12 @@ class @LabelsSelect
defaultLabel
fieldName: $dropdown.data('field-name')
id: (label) ->
+ if $dropdown.hasClass('js-issuable-form-dropdown')
+ if label.id is 0
+ return
+ else
+ return label.id
+
if $dropdown.hasClass("js-filter-submit") and not label.isAny?
label.title
else
@@ -300,6 +308,9 @@ class @LabelsSelect
$selectbox.hide()
# display:block overrides the hide-collapse rule
$value.removeAttr('style')
+
+ return if $dropdown.hasClass('js-issuable-form-dropdown')
+
if $dropdown.hasClass 'js-multiselect'
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
selectedLabels = $dropdown
@@ -321,7 +332,7 @@ class @LabelsSelect
clicked: (label) ->
_this.enableBulkLabelDropdown()
- if $dropdown.hasClass('js-filter-bulk-update')
+ if $dropdown.hasClass('js-filter-bulk-update') or $dropdown.hasClass('js-issuable-form-dropdown')
return
page = $('body').data 'page'
diff --git a/app/assets/javascripts/milestone_select.js.coffee b/app/assets/javascripts/milestone_select.js.coffee
index 8ab03ed93ee..3a036569317 100644
--- a/app/assets/javascripts/milestone_select.js.coffee
+++ b/app/assets/javascripts/milestone_select.js.coffee
@@ -62,7 +62,7 @@ class @MilestoneSelect
title: 'Upcoming'
)
- if extraOptions.length > 2
+ if extraOptions.length > 0
extraOptions.push 'divider'
callback(extraOptions.concat(data))
diff --git a/app/assets/javascripts/projects_list.js.coffee b/app/assets/javascripts/projects_list.js.coffee
index e4c4bf3b273..a7d78d9e461 100644
--- a/app/assets/javascripts/projects_list.js.coffee
+++ b/app/assets/javascripts/projects_list.js.coffee
@@ -5,13 +5,12 @@
this.initPagination()
initSearch: ->
- @timer = null
- $(".projects-list-filter").on('keyup', ->
- clearTimeout(@timer)
- @timer = setTimeout(ProjectsList.filterResults, 500)
- )
+ projectsListFilter = $('.projects-list-filter')
+ debounceFilter = _.debounce ProjectsList.filterResults, 500
+ projectsListFilter.on 'keyup', (e) ->
+ debounceFilter() if projectsListFilter.val() isnt ''
- filterResults: =>
+ filterResults: ->
$('.projects-list-holder').fadeTo(250, 0.5)
form = null
diff --git a/app/assets/javascripts/shortcuts.js.coffee b/app/assets/javascripts/shortcuts.js.coffee
index 3319a67a79d..8c8689bacee 100644
--- a/app/assets/javascripts/shortcuts.js.coffee
+++ b/app/assets/javascripts/shortcuts.js.coffee
@@ -2,9 +2,10 @@ class @Shortcuts
constructor: (skipResetBindings) ->
@enabledHelp = []
Mousetrap.reset() if not skipResetBindings
- Mousetrap.bind('?', @onToggleHelp)
- Mousetrap.bind('s', Shortcuts.focusSearch)
- Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview)
+ Mousetrap.bind '?', @onToggleHelp
+ Mousetrap.bind 's', Shortcuts.focusSearch
+ Mousetrap.bind 'f', (e) => @focusFilter e
+ Mousetrap.bind ['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview
Mousetrap.bind('t', -> Turbolinks.visit(findFileURL)) if findFileURL?
onToggleHelp: (e) =>
@@ -32,10 +33,16 @@ class @Shortcuts
$('.js-more-help-button').remove()
)
+ focusFilter: (e) ->
+ @filterInput ?= $('input[type=search]', '.nav-controls')
+ @filterInput.focus()
+ e.preventDefault()
+
@focusSearch: (e) ->
$('#search').focus()
e.preventDefault()
+
$(document).on 'click.more_help', '.js-more-help-button', (e) ->
$(@).remove()
$('.hidden-shortcut').show()
diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee
index 4e032ab1ff1..c84c4960657 100644
--- a/app/assets/javascripts/users_select.js.coffee
+++ b/app/assets/javascripts/users_select.js.coffee
@@ -151,11 +151,13 @@ class @UsersSelect
# display:block overrides the hide-collapse rule
$value.css('display', '')
- clicked: (user) ->
+ clicked: (user, $el, e) ->
page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index'
isMRIndex = page is page is 'projects:merge_requests:index'
- if $dropdown.hasClass('js-filter-bulk-update')
+ if $dropdown.hasClass('js-filter-bulk-update') or $dropdown.hasClass('js-issuable-form-dropdown')
+ e.preventDefault()
+ selectedId = user.id
return
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
@@ -168,7 +170,8 @@ class @UsersSelect
.closest('.selectbox')
.find("input[name='#{$dropdown.data('field-name')}']").val()
assignTo(selected)
-
+ id: (user) ->
+ user.id
renderRow: (user) ->
username = if user.username then "@#{user.username}" else ""
avatar = if user.avatar_url then user.avatar_url else false
diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index bb8d71fbae8..8b6ddf8ba18 100644
--- a/app/assets/stylesheets/framework/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
@@ -20,6 +20,7 @@
}
&.s16 { width: 16px; height: 16px; margin-right: 6px; }
+ &.s20 { width: 20px; height: 20px; margin-right: 7px; }
&.s24 { width: 24px; height: 24px; margin-right: 8px; }
&.s26 { width: 26px; height: 26px; margin-right: 8px; }
&.s32 { width: 32px; height: 32px; margin-right: 10px; }
diff --git a/app/assets/stylesheets/framework/blank.scss b/app/assets/stylesheets/framework/blank.scss
index d28cda6d62d..540718197e0 100644
--- a/app/assets/stylesheets/framework/blank.scss
+++ b/app/assets/stylesheets/framework/blank.scss
@@ -20,9 +20,12 @@
.blank-state-icon {
padding-bottom: 20px;
+ color: $gray-darkest;
+ font-size: 56px;
- path {
- fill: $gray-darkest;
+ path,
+ polygon {
+ fill: currentColor;
}
}
@@ -37,6 +40,10 @@
margin-top: 0;
margin-bottom: $gl-padding;
font-size: 15px;
+
+ > strong {
+ font-weight: 600;
+ }
}
.blank-state-welcome-title {
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index f8aecd0558d..c1e5305644b 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -270,21 +270,6 @@ table {
}
}
-.dashboard-intro-icon {
- float: left;
- text-align: center;
- font-size: 32px;
- color: #aaa;
- width: 60px;
-}
-
-.dashboard-intro-text {
- display: inline-block;
- margin-left: -60px;
- padding-left: 60px;
- width: 100%;
-}
-
.btn-sign-in {
text-shadow: none;
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index d4e900f80ef..3fd0e12568d 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -56,7 +56,7 @@
position: absolute;
top: 50%;
right: 6px;
- margin-top: -4px;
+ margin-top: -6px;
color: $dropdown-toggle-icon-color;
font-size: 10px;
}
diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss
index d1ff63bd099..3673b81f183 100644
--- a/app/assets/stylesheets/framework/gitlab-theme.scss
+++ b/app/assets/stylesheets/framework/gitlab-theme.scss
@@ -11,7 +11,6 @@
.toggle-nav-collapse,
.pin-nav-btn {
color: $color-light;
- background: $color;
&:hover {
color: $white-light;
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 1a2220f3b40..d52e8f00172 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -17,7 +17,7 @@
height: 100%;
overflow: hidden;
transition: width $sidebar-transition-duration;
- @include box-shadow(2px 0 16px 0 #bbb);
+ @include box-shadow(2px 0 16px 0 $black-transparent);
}
}
@@ -55,10 +55,6 @@
overflow-y: auto;
overflow-x: hidden;
- @media (min-width: $sidebar-breakpoint) {
- bottom: 50px;
- }
-
&.navbar-collapse {
padding: 0 !important;
}
@@ -76,7 +72,7 @@
}
a {
- padding: 7px 15px 7px 12px;
+ padding: 7px $gl-sidebar-padding;
font-size: $gl-font-size;
line-height: 24px;
display: block;
@@ -108,7 +104,7 @@
}
}
-.toggle-nav-collapse {
+.sidebar-action-buttons {
width: $sidebar_width;
position: absolute;
top: 0;
@@ -117,12 +113,37 @@
padding: 5px 0;
font-size: 18px;
line-height: 30px;
+
+ .toggle-nav-collapse {
+ left: 0;
+ }
+
+ .pin-nav-btn {
+ right: 0;
+ display: none;
+
+ @media (min-width: $sidebar-breakpoint) {
+ display: block;
+ }
+
+ .fa {
+ transition: transform .15s;
+ }
+
+ &.is-active {
+ .fa {
+ transform: rotate(90deg);
+ }
+ }
+ }
}
.nav-header-btn {
- padding: 10px 5px;
+ padding: 10px $gl-sidebar-padding;
color: inherit;
transition-duration: .3s;
+ position: absolute;
+ top: 0;
&:hover,
&:focus {
@@ -131,30 +152,6 @@
}
}
-.pin-nav-btn {
- display: none;
- position: absolute;
- left: 0;
- bottom: 0;
- height: 50px;
- width: $sidebar_width;
- line-height: 30px;
-
- @media (min-width: $sidebar-breakpoint) {
- display: block;
- }
-
- .fa {
- transition: transform .15s;
- }
-
- &.is-active {
- .fa {
- transform: rotate(90deg);
- }
- }
-}
-
.page-sidebar-collapsed {
padding-left: 0;
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 4337fab5d87..f0e7002e4cd 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -17,6 +17,7 @@ $focus-border-color: #3aabf0;
$table-border-color: #f0f0f0;
$background-color: #fafafa;
$dark-background-color: #f7f7f7;
+$table-text-gray: #8f8f8f;
/*
* Text
@@ -63,6 +64,7 @@ $gl-btn-padding: 10px;
$gl-input-padding: 10px;
$gl-vert-padding: 6px;
$gl-padding-top: 10px;
+$gl-sidebar-padding: 22px;
/*
* Misc
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index e8f1935d239..99a2cd306cf 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -83,14 +83,6 @@
}
}
-table.builds {
- .build-link {
- a {
- color: $gl-dark-link-color;
- }
- }
-}
-
.build-trace {
background: $ci-output-bg;
color: $ci-text-color;
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index 47bfd144930..3b1e38fc07d 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -162,9 +162,15 @@
}
.filtered-labels {
+ font-size: 0;
+ padding: 12px 16px;
+
.label-row {
+ margin-top: 4px;
+ margin-bottom: 4px;
+
&:not(:last-child) {
- margin-right: 5px;
+ margin-right: 8px;
}
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index d9756b66af0..dba9a7ab3ee 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -73,11 +73,14 @@
color: #888;
}
- &.ci-pending,
- &.ci-running {
+ &.ci-pending {
color: $gl-warning;
}
+ &.ci-running {
+ color: $blue-normal;
+ }
+
&.ci-failed,
&.ci-error {
color: $gl-danger;
@@ -321,6 +324,10 @@
.issuable-form-select-holder {
display: inline-block;
width: 250px;
+
+ .dropdown-menu-toggle {
+ width: 100%;
+ }
}
.table-holder {
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 6128868b670..08da4e290dc 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -1,15 +1,12 @@
.pipelines {
.stage {
- max-width: 100px;
+ max-width: 80px;
+ width: 80px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
- .duration, .finished_at {
- margin: 4px 0;
- }
-
.commit-title {
margin: 0;
}
@@ -22,3 +19,154 @@
margin: 4px;
}
}
+
+.content-list {
+
+ &.pipelines,
+ &.builds-content-list {
+ width: 100%;
+ overflow: auto;
+ }
+}
+
+.table.builds {
+ min-width: 1100px;
+
+ tr {
+ th {
+ padding: 16px;
+ border: none;
+ }
+ }
+
+ tbody {
+ border-top-width: 1px;
+ }
+
+ .commit-link {
+
+ a:hover {
+ text-decoration: none;
+ }
+ }
+
+ .branch-commit {
+
+ .branch-name {
+ margin-left: 8px;
+ font-weight: bold;
+ max-width: 180px;
+ overflow: hidden;
+ display: inline-block;
+ white-space: nowrap;
+ vertical-align: top;
+ text-overflow: ellipsis;
+ }
+
+ svg {
+ margin: 0 6px;
+ height: 14px;
+ width: auto;
+ vertical-align: middle;
+ }
+
+ .commit-id {
+ color: $gl-link-color;
+ margin-right: 8px;
+ }
+
+ .commit-title {
+ margin-top: 4px;
+ max-width: 320px;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ }
+
+ .avatar {
+ margin-left: 0;
+ }
+
+ .label {
+ margin-right: 4px;
+ }
+
+ .label-container {
+ font-size: 0;
+
+ .label {
+ margin-top: 5px;
+ }
+ }
+ }
+
+ .duration,
+ .finished-at {
+ color: $table-text-gray;
+ margin: 4px 0;
+
+ .fa {
+ font-size: 12px;
+ }
+
+ svg {
+ height: 12px;
+ width: auto;
+ vertical-align: middle;
+ }
+
+ .fa,
+ svg {
+ margin-right: 5px;
+ }
+ }
+
+ .pipeline-actions {
+
+ .btn {
+ margin: 0;
+ color: $table-text-gray;
+ }
+
+ .cancel-retry-btns {
+ .btn:not(:first-child) {
+ margin-left: 8px;
+ }
+ }
+
+ .dropdown-toggle,
+ .dropdown-menu {
+ color: $table-text-gray;
+
+ .fa {
+ color: $table-text-gray;
+ margin-right: 6px;
+ font-size: 14px;
+ }
+ }
+
+ .btn-remove {
+ color: $white-light;
+ }
+
+ .btn-group {
+ &.open {
+ .btn-default {
+ background-color: $white-normal;
+ border-color: $border-white-normal;
+ }
+ }
+ }
+ }
+
+ .build-link {
+
+ a {
+ color: $gl-dark-link-color;
+ }
+ }
+
+ .btn-group.open .dropdown-toggle {
+ box-shadow: none;
+ }
+}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 5be911dc562..4e6d732fb6d 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -639,3 +639,9 @@ pre.light-well {
width: 300px;
}
}
+
+.compare-form-group {
+ .dropdown-menu {
+ width: 300px;
+ }
+}
diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss
index 2370d35924e..c6b053150be 100644
--- a/app/assets/stylesheets/pages/status.scss
+++ b/app/assets/stylesheets/pages/status.scss
@@ -32,11 +32,15 @@
border-color: $gl-gray;
}
- &.ci-pending,
- &.ci-running {
+ &.ci-pending {
color: $gl-warning;
border-color: $gl-warning;
}
+
+ &.ci-running {
+ color: $blue-normal;
+ border-color: $blue-normal;
+ }
}
.ci-status-icon-success {
@@ -45,10 +49,12 @@
.ci-status-icon-failed {
color: $gl-danger;
}
- .ci-status-icon-running,
.ci-status-icon-pending {
color: $gl-warning;
}
+ .ci-status-icon-running {
+ color: $blue-normal;
+ }
.ci-status-icon-canceled,
.ci-status-icon-disabled,
.ci-status-icon-not-found,
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 5b61270daa8..42a20e9775f 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -23,12 +23,11 @@
}
&:hover {
- cursor: pointer;
-
td {
background-color: $row-hover;
border-top: 1px solid $row-hover-border;
border-bottom: 1px solid $row-hover-border;
+ cursor: pointer;
}
}
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index f35d631df0c..f54c79c2e37 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -107,7 +107,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# Only allow properly saved users to login.
if @user.persisted? && @user.valid?
log_audit_event(@user, with: oauth['provider'])
- sign_in_and_redirect(@user)
+ if @user.two_factor_enabled?
+ prompt_for_two_factor(@user)
+ else
+ sign_in_and_redirect(@user)
+ end
else
error_message = @user.errors.full_messages.to_sentence
diff --git a/app/finders/todos_finder.rb b/app/finders/todos_finder.rb
index 1a8f490355b..ff866c2faa5 100644
--- a/app/finders/todos_finder.rb
+++ b/app/finders/todos_finder.rb
@@ -37,7 +37,7 @@ class TodosFinder
private
def action_id?
- action_id.present? && [Todo::ASSIGNED, Todo::MENTIONED, Todo::BUILD_FAILED, Todo::MARKED].include?(action_id.to_i)
+ action_id.present? && Todo::ACTION_NAMES.has_key?(action_id.to_i)
end
def action_id
diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb
index 950f323e383..e12a1052988 100644
--- a/app/helpers/appearances_helper.rb
+++ b/app/helpers/appearances_helper.rb
@@ -31,7 +31,7 @@ module AppearancesHelper
end
end
- def navbar_icon(icon_name, size: 16)
+ def custom_icon(icon_name, size: 16)
render "shared/icons/#{icon_name}.svg", size: size
end
end
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
index 8e4ae1e6aec..e6c99c9959e 100644
--- a/app/helpers/ci_status_helper.rb
+++ b/app/helpers/ci_status_helper.rb
@@ -29,8 +29,10 @@ module CiStatusHelper
'check'
when 'failed'
'close'
- when 'running', 'pending'
+ when 'pending'
'clock-o'
+ when 'running'
+ 'spinner'
else
'circle'
end
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 294b7e92b8d..a3a8c7d5ff9 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -9,7 +9,7 @@ module IssuablesHelper
def multi_label_name(current_labels, default_label)
# current_labels may be a string from before
- if current_labels.is_a?(Array)
+ if current_labels.is_a?(Array) && current_labels.any?
if current_labels.count > 1
"#{current_labels[0]} +#{current_labels.count - 1} more"
else
@@ -61,7 +61,7 @@ module IssuablesHelper
output = content_tag :strong, "#{text} #{issuable.to_reference}", class: "identifier"
output << " opened #{time_ago_with_tooltip(issuable.created_at)} by ".html_safe
output << content_tag(:strong) do
- author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs")
+ author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs", tooltip: true)
author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "hidden-sm hidden-md hidden-lg")
end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 3bbbb26cff2..a733dff1579 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -19,7 +19,7 @@ module ProjectsHelper
end
def link_to_member(project, author, opts = {}, &block)
- default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" }
+ default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name", tooltip: false }
opts = default_opts.merge(opts)
return "(deleted)" unless author
@@ -33,7 +33,8 @@ module ProjectsHelper
if opts[:by_username]
author_html << content_tag(:span, sanitize("@#{author.username}"), class: opts[:author_class]) if opts[:name]
else
- author_html << content_tag(:span, sanitize(author.name), class: opts[:author_class]) if opts[:name]
+ tooltip_data = { placement: 'top' }
+ author_html << content_tag(:span, sanitize(author.name), class: [opts[:author_class], ('has-tooltip' if opts[:tooltip])], title: (author.to_reference if opts[:tooltip]), data: (tooltip_data if opts[:tooltip])) if opts[:name]
end
author_html << capture(&block) if block
@@ -60,7 +61,7 @@ module ProjectsHelper
project_link = link_to simple_sanitize(project.name), project_path(project), { class: "project-item-select-holder" }
if current_user
- project_link << icon("chevron-down", class: "dropdown-toggle-caret js-projects-dropdown-toggle", data: { target: ".js-dropdown-menu-projects", toggle: "dropdown" })
+ project_link << icon("chevron-down", class: "dropdown-toggle-caret js-projects-dropdown-toggle", aria: { label: "Toggle switch project dropdown" }, data: { target: ".js-dropdown-menu-projects", toggle: "dropdown" })
end
full_title = "#{namespace_link} / #{project_link}".html_safe
diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb
index 0925760e69c..e3a208f826a 100644
--- a/app/helpers/todos_helper.rb
+++ b/app/helpers/todos_helper.rb
@@ -13,6 +13,7 @@ module TodosHelper
when Todo::MENTIONED then 'mentioned you on'
when Todo::BUILD_FAILED then 'The build failed for your'
when Todo::MARKED then 'added a todo for'
+ when Todo::APPROVAL_REQUIRED then 'set you as an approver for'
end
end
diff --git a/app/models/label.rb b/app/models/label.rb
index dc5586f5756..35e678001dc 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -52,14 +52,17 @@ class Label < ActiveRecord::Base
# This pattern supports cross-project references.
#
def self.reference_pattern
+ # NOTE: The id pattern only matches when all characters on the expression
+ # are digits, so it will match ~2 but not ~2fa because that's probably a
+ # label name and we want it to be matched as such.
@reference_pattern ||= %r{
(#{Project.reference_pattern})?
#{Regexp.escape(reference_prefix)}
(?:
- (?<label_id>\d+) | # Integer-based label ID, or
+ (?<label_id>\d+(?!\S\w)\b) | # Integer-based label ID, or
(?<label_name>
- [A-Za-z0-9_\-\?&]+ | # String-based single-word label title, or
- "[^,]+" # String-based multi-word label surrounded in quotes
+ [A-Za-z0-9_\-\?\.&]+ | # String-based single-word label title, or
+ ".+?" # String-based multi-word label surrounded in quotes
)
)
}x
diff --git a/app/models/project.rb b/app/models/project.rb
index a66b750cd48..e7b9835692d 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -162,7 +162,7 @@ class Project < ActiveRecord::Base
validates :namespace, presence: true
validates_uniqueness_of :name, scope: :namespace_id
validates_uniqueness_of :path, scope: :namespace_id
- validates :import_url, addressable_url: true, if: :external_import?
+ validates :import_url, addressable_url: true, if: :import_url
validates :star_count, numericality: { greater_than_or_equal_to: 0 }
validate :check_limit, on: :create
validate :avatar_type,
@@ -464,8 +464,8 @@ class Project < ActiveRecord::Base
return super(value) unless Gitlab::UrlSanitizer.valid?(value)
import_url = Gitlab::UrlSanitizer.new(value)
- create_or_update_import_data(credentials: import_url.credentials)
super(import_url.sanitized_url)
+ create_or_update_import_data(credentials: import_url.credentials)
end
def import_url
@@ -477,7 +477,13 @@ class Project < ActiveRecord::Base
end
end
+ def valid_import_url?
+ valid? || errors.messages[:import_url].nil?
+ end
+
def create_or_update_import_data(data: nil, credentials: nil)
+ return unless valid_import_url?
+
project_import_data = import_data || build_import_data
if data
project_import_data.data ||= {}
diff --git a/app/models/todo.rb b/app/models/todo.rb
index ac3fdbc7f3b..8d7a5965aa1 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -1,14 +1,16 @@
class Todo < ActiveRecord::Base
- ASSIGNED = 1
- MENTIONED = 2
- BUILD_FAILED = 3
- MARKED = 4
+ ASSIGNED = 1
+ MENTIONED = 2
+ BUILD_FAILED = 3
+ MARKED = 4
+ APPROVAL_REQUIRED = 5 # This is an EE-only feature
ACTION_NAMES = {
ASSIGNED => :assigned,
MENTIONED => :mentioned,
BUILD_FAILED => :build_failed,
- MARKED => :marked
+ MARKED => :marked,
+ APPROVAL_REQUIRED => :approval_required
}
belongs_to :author, class_name: "User"
diff --git a/app/models/user.rb b/app/models/user.rb
index 79c670cb35a..7a72c202150 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -87,7 +87,7 @@ class User < ActiveRecord::Base
has_many :builds, dependent: :nullify, class_name: 'Ci::Build'
has_many :todos, dependent: :destroy
has_many :notification_settings, dependent: :destroy
- has_many :award_emoji, as: :awardable, dependent: :destroy
+ has_many :award_emoji, dependent: :destroy
#
# Validations
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index 163ebf26c84..cdad0426b02 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -43,7 +43,7 @@ module Projects
def import_repository
begin
gitlab_shell.import_repository(project.repository_storage_path, project.path_with_namespace, project.import_url)
- rescue Gitlab::Shell::Error => e
+ rescue => e
raise Error, "Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}"
end
end
diff --git a/app/views/admin/builds/_build.html.haml b/app/views/admin/builds/_build.html.haml
index 967151bc33b..ce818c30c30 100644
--- a/app/views/admin/builds/_build.html.haml
+++ b/app/views/admin/builds/_build.html.haml
@@ -1,30 +1,40 @@
- project = build.project
-%tr.build
+%tr.build.commit
%td.status
= ci_status_with_icon(build.status)
- %td.build-link
- - if can?(current_user, :read_build, build.project)
- = link_to namespace_project_build_url(build.project.namespace, build.project, build) do
- %strong Build ##{build.id}
- - else
- %strong Build ##{build.id}
+ %td
+ .branch-commit
+ - if can?(current_user, :read_build, build.project)
+ = link_to namespace_project_build_url(build.project.namespace, build.project, build) do
+ %span.build-link ##{build.id}
+ - else
+ %span.build-link ##{build.id}
- - if build.stuck?
- %i.fa.fa-warning.text-warning
+ - if build.stuck?
+ %i.fa.fa-warning.text-warning
- %td
- - if project
- = link_to project.name_with_namespace, admin_namespace_project_path(project.namespace, project)
+ - if build.ref
+ = link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref), class: "monospace branch-name"
+ - else
+ .light none
+ = custom_icon("icon_commit")
- %td
- = link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace"
+ = link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace commit-id"
+
+ .label-container
+ - if build.tags.any?
+ - build.tags.each do |tag|
+ %span.label.label-primary
+ = tag
+ - if build.try(:trigger_request)
+ %span.label.label-info triggered
+ - if build.try(:allow_failure)
+ %span.label.label-danger allowed to fail
%td
- - if build.ref
- = link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref)
- - else
- .light none
+ - if project
+ = link_to project.name_with_namespace, admin_namespace_project_path(project.namespace, project)
%td
- if build.try(:runner)
@@ -36,22 +46,15 @@
#{build.stage} / #{build.name}
%td
- - if build.tags.any?
- - build.tags.each do |tag|
- %span.label.label-primary
- = tag
- - if build.try(:trigger_request)
- %span.label.label-info triggered
- - if build.try(:allow_failure)
- %span.label.label-danger allowed to fail
-
- %td.duration
- if build.duration
- #{duration_in_words(build.finished_at, build.started_at)}
+ %p.duration
+ = custom_icon("icon_timer")
+ = duration_in_numbers(build.finished_at, build.started_at)
- %td.timestamp
- if build.finished_at
- %span #{time_ago_with_tooltip(build.finished_at)}
+ %p.finished-at
+ = icon("calendar")
+ %span #{time_ago_with_tooltip(build.finished_at)}
- if defined?(coverage) && coverage
%td.coverage
diff --git a/app/views/admin/builds/index.html.haml b/app/views/admin/builds/index.html.haml
index 1e60205f91a..9ea3cca0ecb 100644
--- a/app/views/admin/builds/index.html.haml
+++ b/app/views/admin/builds/index.html.haml
@@ -27,7 +27,7 @@
.row-content-block.second-block
#{(@scope || 'all').capitalize} builds
- %ul.content-list
+ %ul.content-list.builds-content-list
- if @builds.blank?
%li
.nothing-here-block No builds to show
@@ -37,15 +37,11 @@
%thead
%tr
%th Status
- %th Build ID
- %th Project
%th Commit
- %th Ref
+ %th Project
%th Runner
%th Name
- %th Tags
- %th Duration
- %th Finished at
+ %th
%th
- @builds.each do |build|
diff --git a/app/views/dashboard/projects/_zero_authorized_projects.html.haml b/app/views/dashboard/projects/_zero_authorized_projects.html.haml
index d54c7cad7be..40c70fa3025 100644
--- a/app/views/dashboard/projects/_zero_authorized_projects.html.haml
+++ b/app/views/dashboard/projects/_zero_authorized_projects.html.haml
@@ -1,53 +1,46 @@
- publicish_project_count = ProjectsFinder.new.execute(current_user).count
-%h3.page-title Welcome to GitLab!
-%p.light Self hosted Git management application.
-%hr
-%div
- .dashboard-intro-icon
- %i.fa.fa-bookmark-o
- .dashboard-intro-text
- %p.slead
- You don't have access to any projects right now.
- %br
- - if current_user.can_create_project?
- You can create up to
- %strong= pluralize(number_with_delimiter(current_user.projects_limit), "project") + "."
- - else
- If you are added to a project, it will be displayed here.
-
+.blank-state.blank-state-welcome
+ %h2.blank-state-welcome-title
+ Welcome to GitLab
+ %p.blank-state-text
+ Code, test, and deploy together
+.blank-state
+ .blank-state-icon
+ = navbar_icon("project", size: 50)
+ %h3.blank-state-title
+ You don't have access to any projects right now
+ %p.blank-state-text
- if current_user.can_create_project?
- .link_holder
- = link_to new_project_path, class: "btn btn-new" do
- = icon('plus')
- New Project
+ You can create up to
+ %strong= number_with_delimiter(current_user.projects_limit)
+ = succeed "." do
+ = "project".pluralize(current_user.projects_limit)
+ - else
+ If you are added to a project, it will be displayed here.
+ - if current_user.can_create_project?
+ = link_to new_project_path, class: "btn btn-new" do
+ New project
- if current_user.can_create_group?
- %hr
- %div
- .dashboard-intro-icon
- %i.fa.fa-users
- .dashboard-intro-text
- %p.slead
- You can create a group for several dependent projects.
- %br
- Groups are the best way to manage projects and members.
- .link_holder
- = link_to new_group_path, class: "btn btn-new" do
- %i.fa.fa-plus
- New Group
+ .blank-state
+ .blank-state-icon
+ = navbar_icon("group", size: 50)
+ %h3.blank-state-title
+ You can create a group for several dependent projects.
+ %p.blank-state-text
+ Groups are the best way to manage projects and members.
+ = link_to new_group_path, class: "btn btn-new" do
+ New group
-if publicish_project_count > 0
- %hr
- %div
- .dashboard-intro-icon
- %i.fa.fa-globe
- .dashboard-intro-text
- %p.slead
- There are
- %strong= number_with_delimiter(publicish_project_count)
- public projects on this server.
- %br
- Public projects are an easy way to allow everyone to have read-only access.
- .link_holder
- = link_to trending_explore_projects_path, class: "btn btn-new" do
- Browse public projects
+ .blank-state
+ .blank-state-icon
+ = icon("globe")
+ %h3.blank-state-title
+ There are
+ = number_with_delimiter(publicish_project_count)
+ public projects on this server.
+ %p.blank-state-text
+ Public projects are an easy way to allow everyone to have read-only access.
+ = link_to trending_explore_projects_path, class: "btn btn-new" do
+ Browse projects
diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml
index 4565e752c1f..4f36a4a1c73 100644
--- a/app/views/dashboard/projects/index.html.haml
+++ b/app/views/dashboard/projects/index.html.haml
@@ -5,7 +5,8 @@
- page_title "Projects"
- header_title "Projects", dashboard_projects_path
-= render 'dashboard/projects_head'
+- if @projects.any? || params[:filter_projects]
+ = render 'dashboard/projects_head'
- if @last_push
= render "events/event_last_push", event: @last_push
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index fc42e5dcc66..4e340b6ec16 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -9,14 +9,14 @@
%span
To do
%span.badge
- = todos_pending_count
+ = number_with_delimiter(todos_pending_count)
- todo_done_active = ('active' if params[:state] == 'done')
%li{class: "todos-done #{todo_done_active}"}
= link_to todos_filter_path(state: 'done') do
%span
Done
%span.badge
- = todos_done_count
+ = number_with_delimiter(todos_done_count)
.nav-controls
- if @todos.any?(&:pending?)
diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml
index 8cc0b59edeb..ce4536ebdc6 100644
--- a/app/views/help/_shortcuts.html.haml
+++ b/app/views/help/_shortcuts.html.haml
@@ -20,6 +20,10 @@
%td Focus Search
%tr
%td.shortcut
+ .key f
+ %td Focus Filter
+ %tr
+ %td.shortcut
.key ?
%td Show/hide this dialog
%tr
diff --git a/app/views/layouts/_collapse_button.html.haml b/app/views/layouts/_collapse_button.html.haml
deleted file mode 100644
index 8c140a5943e..00000000000
--- a/app/views/layouts/_collapse_button.html.haml
+++ /dev/null
@@ -1,3 +0,0 @@
-= link_to '#', class: 'nav-header-btn text-center toggle-nav-collapse', title: "Open/Close" do
- %span.sr-only Toggle navigation
- = icon('bars')
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 8596bbfdef6..a1a71c2fb33 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -1,6 +1,13 @@
.page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
.sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
- = render partial: 'layouts/collapse_button'
+ .sidebar-action-buttons
+ = link_to '#', class: 'nav-header-btn toggle-nav-collapse', title: "Open/Close" do
+ %span.sr-only Toggle navigation
+ = icon('bars')
+ = link_to '#', class: "nav-header-btn pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: {placement: 'right', container: 'body'} do
+ %span.sr-only Toggle navigation pinning
+ = icon('fw thumb-tack')
+
- if defined?(sidebar) && sidebar
= render "layouts/nav/#{sidebar}"
- elsif current_user
@@ -8,9 +15,6 @@
- else
= render 'layouts/nav/explore'
- = link_to '#', class: "nav-header-btn text-center pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: {placement: 'right', container: 'body'} do
- %span.sr-only Toggle navigation pinning
- = icon('thumb-tack')
- if defined?(nav) && nav
.layout-nav
.container-fluid
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 11cee421a99..94c53882623 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -1,7 +1,7 @@
%header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class }
%div{ class: fluid_layout ? "container-fluid" : "container-fluid" }
.header-content
- %button.side-nav-toggle{type: 'button'}
+ %button.side-nav-toggle{ type: 'button', "aria-label" => "Toggle global navigation" }
%span.sr-only Toggle navigation
= icon('bars')
%button.navbar-toggle{type: 'button'}
@@ -13,25 +13,25 @@
%li.hidden-sm.hidden-xs
= render 'layouts/search' unless current_controller?(:search)
%li.visible-sm.visible-xs
- = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = link_to search_path, title: 'Search', aria: { label: "Search" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('search')
- if current_user
- if session[:impersonator_id]
%li.impersonation
- = link_to admin_impersonation_path, method: :delete, title: 'Stop Impersonation', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
+ = link_to admin_impersonation_path, method: :delete, title: "Stop Impersonation", aria: { label: 'Stop Impersonation' }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= icon('user-secret fw')
- if current_user.is_admin?
%li
- = link_to admin_root_path, title: 'Admin Area', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = link_to admin_root_path, title: 'Admin Area', aria: { label: "Admin Area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('wrench fw')
%li
- = link_to dashboard_todos_path, title: 'Todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('bell fw')
%span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0) }
= todos_pending_count
- if current_user.can_create_project?
%li
- = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = link_to new_project_path, title: 'New project', aria: { label: "New project" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('plus fw')
- if Gitlab::Sherlock.enabled?
%li
@@ -45,12 +45,12 @@
.dropdown-menu-nav.dropdown-menu-align-right
%ul
%li
- = link_to "Profile", current_user, class: 'profile-link', data: { user: current_user.username }
+ = link_to "Profile", current_user, class: 'profile-link', aria: { label: "Profile" }, data: { user: current_user.username }
%li
- = link_to "Profile Settings", profile_path
+ = link_to "Profile Settings", profile_path, aria: { label: "Profile Settings" }
%li.divider
%li
- = link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link", title: 'Sign out'
+ = link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link", aria: { label: "Sign out" }
- else
%li
%div
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 52e41b1a857..21668698814 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -1,64 +1,44 @@
%ul.nav.nav-sidebar
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do
= link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
- .icon-container
- = navbar_icon('project')
%span
Projects
= nav_link(controller: :todos) do
= link_to dashboard_todos_path, title: 'Todos' do
- .icon-container
- = icon('bell fw')
%span
Todos
%span.count= number_with_delimiter(todos_pending_count)
= nav_link(path: 'dashboard#activity') do
= link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
- .icon-container
- = navbar_icon('activity')
%span
Activity
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
= link_to dashboard_groups_path, title: 'Groups' do
- .icon-container
- = navbar_icon('group')
%span
Groups
= nav_link(controller: 'dashboard/milestones') do
= link_to dashboard_milestones_path, title: 'Milestones' do
- .icon-container
- = navbar_icon('milestones')
%span
Milestones
= nav_link(path: 'dashboard#issues') do
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
- .icon-container
- = navbar_icon('issues')
%span
Issues
%span.count= number_with_delimiter(current_user.assigned_issues.opened.count)
= nav_link(path: 'dashboard#merge_requests') do
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
- .icon-container
- = navbar_icon('mr')
%span
Merge Requests
%span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count)
= nav_link(controller: :snippets) do
= link_to dashboard_snippets_path, title: 'Snippets' do
- .icon-container
- = icon('clipboard fw')
%span
Snippets
= nav_link(controller: :help) do
= link_to help_path, title: 'Help' do
- .icon-container
- = icon('question-circle fw')
%span
Help
= nav_link(html_options: {class: profile_tab_class}) do
= link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do
- .icon-container
- = icon('user fw')
%span
Profile Settings
diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml
index 381b3754cd5..85c31dfd918 100644
--- a/app/views/projects/builds/index.html.haml
+++ b/app/views/projects/builds/index.html.haml
@@ -36,7 +36,7 @@
= link_to ci_lint_path, class: 'btn btn-default' do
%span CI Lint
- %ul.content-list
+ %ul.content-list.builds-content-list
- if @builds.blank?
%li
.nothing-here-block No builds to show
@@ -46,14 +46,10 @@
%thead
%tr
%th Status
- %th Build ID
%th Commit
- %th Ref
%th Stage
%th Name
- %th Tags
- %th Duration
- %th Finished at
+ %th
- if @project.build_coverage_enabled?
%th Coverage
%th
diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml
index 5bd6e3f0ebc..e1b42b2cfa5 100644
--- a/app/views/projects/ci/builds/_build.html.haml
+++ b/app/views/projects/ci/builds/_build.html.haml
@@ -1,32 +1,45 @@
-%tr.build
+%tr.build.commit
%td.status
- if can?(current_user, :read_build, build)
= ci_status_with_icon(build.status, namespace_project_build_url(build.project.namespace, build.project, build))
- else
= ci_status_with_icon(build.status)
- %td.build-link
- - if can?(current_user, :read_build, build)
- = link_to namespace_project_build_url(build.project.namespace, build.project, build) do
- %strong ##{build.id}
- - else
- %strong ##{build.id}
+ %td
+ .branch-commit
+ - if can?(current_user, :read_build, build)
+ = link_to namespace_project_build_url(build.project.namespace, build.project, build) do
+ %span ##{build.id}
+ - else
+ %span ##{build.id}
- - if build.stuck?
- = icon('warning', class: 'text-warning has-tooltip', title: 'Build is stuck. Check runners.')
- - if defined?(retried) && retried
- = icon('warning', class: 'text-warning has-tooltip', title: 'Build was retried.')
+ - if build.stuck?
+ = icon('warning', class: 'text-warning has-tooltip', title: 'Build is stuck. Check runners.')
+ - if defined?(retried) && retried
+ = icon('warning', class: 'text-warning has-tooltip', title: 'Build was retried.')
- - if defined?(commit_sha) && commit_sha
- %td
- = link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace"
+ - if defined?(ref) && ref
+ - if build.ref
+ = link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref), class: "monospace branch-name"
+ - else
+ .light none
+ = custom_icon("icon_commit")
+
+ - if defined?(commit_sha) && commit_sha
+ = link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "commit-id monospace"
+
+ .label-container
+ - if build.tags.any?
+ - build.tags.each do |tag|
+ %span.label.label-primary
+ = tag
+ - if build.try(:trigger_request)
+ %span.label.label-info triggered
+ - if build.try(:allow_failure)
+ %span.label.label-danger allowed to fail
+ - if defined?(retried) && retried
+ %span.label.label-warning retried
- - if defined?(ref) && ref
- %td
- - if build.ref
- = link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref)
- - else
- .light none
- if defined?(runner) && runner
%td
@@ -43,25 +56,14 @@
= build.name
%td
- .label-container
- - if build.tags.any?
- - build.tags.each do |tag|
- %span.label.label-primary
- = tag
- - if build.try(:trigger_request)
- %span.label.label-info triggered
- - if build.try(:allow_failure)
- %span.label.label-danger allowed to fail
- - if defined?(retried) && retried
- %span.label.label-warning retried
-
- %td.duration
- if build.duration
- #{duration_in_words(build.finished_at, build.started_at)}
-
- %td.timestamp
+ %p.duration
+ = custom_icon("icon_timer")
+ = duration_in_numbers(build.finished_at, build.started_at)
- if build.finished_at
- %span #{time_ago_with_tooltip(build.finished_at)}
+ %p.finished-at
+ = icon("calendar")
+ %span #{time_ago_with_tooltip(build.finished_at)}
- if defined?(coverage) && coverage
%td.coverage
@@ -79,4 +81,4 @@
= icon('remove', class: 'cred')
- elsif defined?(allow_retry) && allow_retry && build.retryable?
= link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-build' do
- = icon('refresh')
+ = icon('repeat')
diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml
index af8dd5cd02c..b53a8633937 100644
--- a/app/views/projects/ci/pipelines/_pipeline.html.haml
+++ b/app/views/projects/ci/pipelines/_pipeline.html.haml
@@ -1,17 +1,18 @@
- status = pipeline.status
%tr.commit
%td.commit-link
- = link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: "ci-status ci-#{status}" do
- = ci_icon_for_status(status)
- %strong ##{pipeline.id}
+ = link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline.id) do
+ = ci_status_with_icon(status)
+
%td
- %div.branch-commit
+ .branch-commit
+ = link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline.id) do
+ %span ##{pipeline.id}
- if pipeline.ref
- = link_to pipeline.ref, namespace_project_commits_path(@project.namespace, @project, pipeline.ref), class: "monospace"
- &middot;
+ = link_to pipeline.ref, namespace_project_commits_path(@project.namespace, @project, pipeline.ref), class: "monospace branch-name"
+ = custom_icon("icon_commit")
= link_to pipeline.short_sha, namespace_project_commit_path(@project.namespace, @project, pipeline.sha), class: "commit-id monospace"
- &nbsp;
- if pipeline.tag?
%span.label.label-primary tag
- elsif pipeline.latest?
@@ -25,6 +26,7 @@
%p.commit-title
- if commit = pipeline.commit
+ = commit_author_avatar(commit, size: 20)
= link_to_gfm truncate(commit.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit.id), class: "commit-row-message"
- else
Cant find HEAD commit for this branch
@@ -45,27 +47,43 @@
%td
- if pipeline.started_at && pipeline.finished_at
%p.duration
+ = custom_icon("icon_timer")
= duration_in_numbers(pipeline.finished_at, pipeline.started_at)
+ - if pipeline.finished_at
+ %p.finished-at
+ = icon("calendar")
+ #{time_ago_with_tooltip(pipeline.finished_at)}
- %td
+ %td.pipeline-actions
.controls.hidden-xs.pull-right
- artifacts = pipeline.builds.latest.select { |b| b.artifacts? }
- if artifacts.present?
- .dropdown.inline.build-artifacts
- %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
- = icon('download')
- %b.caret
- %ul.dropdown-menu.dropdown-menu-align-right
- - artifacts.each do |build|
+ .btn-group.inline
+ .btn-group
+ %a.dropdown-toggle.btn.btn-default{type: 'button', 'data-toggle' => 'dropdown'}
+ = icon("play")
+ %b.caret
+ %ul.dropdown-menu.dropdown-menu-align-right
%li
- = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, build), rel: 'nofollow' do
- = icon("download")
- %span Download '#{build.name}' artifacts
+ = link_to '#' do
+ = icon("play")
+ %span Deploy to production
+ .btn-group
+ %a.dropdown-toggle.btn.btn-default.build-artifacts{type: 'button', 'data-toggle' => 'dropdown'}
+ = icon("download")
+ %b.caret
+ %ul.dropdown-menu.dropdown-menu-align-right
+ - artifacts.each do |build|
+ %li
+ = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, build), rel: 'nofollow' do
+ = icon("download")
+ %span Download '#{build.name}' artifacts
- if can?(current_user, :update_pipeline, @project)
- - if pipeline.retryable?
- = link_to retry_namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: 'btn has-tooltip', title: "Retry", method: :post do
- = icon("repeat")
- - if pipeline.cancelable?
- = link_to cancel_namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: 'btn btn-remove has-tooltip', title: "Cancel", method: :post do
- = icon("remove")
+ .cancel-retry-btns
+ - if pipeline.retryable?
+ = link_to retry_namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: 'btn has-tooltip', title: "Retry", method: :post do
+ = icon("repeat")
+ - if pipeline.cancelable?
+ = link_to cancel_namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: 'btn btn-remove has-tooltip', title: "Cancel", method: :post do
+ = icon("remove")
diff --git a/app/views/projects/commit/_pipeline.html.haml b/app/views/projects/commit/_pipeline.html.haml
index 0411137b7c6..41fd5459429 100644
--- a/app/views/projects/commit/_pipeline.html.haml
+++ b/app/views/projects/commit/_pipeline.html.haml
@@ -42,9 +42,7 @@
%th Status
%th Build ID
%th Name
- %th Tags
- %th Duration
- %th Finished at
+ %th
- if pipeline.project.build_coverage_enabled?
%th Coverage
%th
diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml
index dd590a4b8ec..af09b3418ea 100644
--- a/app/views/projects/compare/_form.html.haml
+++ b/app/views/projects/compare/_form.html.haml
@@ -2,15 +2,17 @@
.clearfix
- if params[:to] && params[:from]
= link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has-tooltip', title: 'Switch base of comparison'}
- .form-group
+ .form-group.dropdown.compare-form-group.js-compare-from-dropdown
.input-group.inline-input-group
%span.input-group-addon from
- = text_field_tag :from, params[:from], class: "form-control", required: true
+ = text_field_tag :from, params[:from], class: "form-control js-compare-dropdown", required: true, data: { refs_url: refs_namespace_project_path(@project.namespace, @project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from].presence }
+ = render "ref_dropdown"
= "..."
- .form-group
+ .form-group.dropdown.compare-form-group.js-compare-to-dropdown
.input-group.inline-input-group
%span.input-group-addon to
- = text_field_tag :to, params[:to], class: "form-control", required: true
+ = text_field_tag :to, params[:to], class: "form-control js-compare-dropdown", required: true, data: { refs_url: refs_namespace_project_path(@project.namespace, @project), toggle: "dropdown", target: ".js-compare-to-dropdown", selected: params[:to].presence }
+ = render "ref_dropdown"
&nbsp;
= button_tag "Compare", class: "btn btn-create commits-compare-btn"
- if @merge_request.present?
@@ -19,11 +21,3 @@
= link_to create_mr_path, class: 'prepend-left-10 btn' do
= icon("plus")
Create Merge Request
-
-:javascript
- var availableTags = #{@project.repository.ref_names.to_json};
-
- $("#from, #to").autocomplete({
- source: availableTags,
- minLength: 1
- });
diff --git a/app/views/projects/compare/_ref_dropdown.html.haml b/app/views/projects/compare/_ref_dropdown.html.haml
new file mode 100644
index 00000000000..c604c6d0135
--- /dev/null
+++ b/app/views/projects/compare/_ref_dropdown.html.haml
@@ -0,0 +1,4 @@
+.dropdown-menu.dropdown-menu-selectable
+ = dropdown_title "Select branch/tag"
+ = dropdown_content
+ = dropdown_loading
diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml
index 5bc5c71283e..542827b2f15 100644
--- a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml
+++ b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml
@@ -50,10 +50,12 @@
%td.duration
- if generic_commit_status.duration
+ = icon("clock-o")
#{duration_in_words(generic_commit_status.finished_at, generic_commit_status.started_at)}
%td.timestamp
- if generic_commit_status.finished_at
+ = icon("calendar")
%span #{time_ago_with_tooltip(generic_commit_status.finished_at)}
- if defined?(coverage) && coverage
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index 312bd86ed04..7612fe3719a 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -32,7 +32,7 @@
Code, test, and deploy together
.blank-state
.blank-state-icon
- = navbar_icon("issues", size: 50)
+ = custom_icon("issues", size: 50)
%h3.blank-state-title
You don't have any issues right now.
%p.blank-state-text
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 9b00bdedc27..c72d0140bb9 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -154,4 +154,9 @@
$('.import_gitlab_project').attr('disabled',true);
$('.import_gitlab_project').attr('title', 'Project path required.');
}
- })
+ });
+
+ $('.import_git').click(function( event ) {
+ $projectImportUrl = $('#project_import_url')
+ $projectImportUrl.attr('disabled', !$projectImportUrl.attr('disabled'))
+ }); \ No newline at end of file
diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml
index 7c225e2b282..5f466bdbac2 100644
--- a/app/views/projects/pipelines/index.html.haml
+++ b/app/views/projects/pipelines/index.html.haml
@@ -28,7 +28,7 @@
.nav-controls
- if can? current_user, :create_pipeline, @project
= link_to new_namespace_project_pipeline_path(@project.namespace, @project), class: 'btn btn-create' do
- New pipeline
+ Run pipeline
- unless @repository.gitlab_ci_yml
= link_to 'Get started with Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
@@ -45,13 +45,13 @@
.table-holder
%table.table.builds
%tbody
- %th ID
+ %th Status
%th Commit
- stages.each do |stage|
%th.stage
%span.has-tooltip{ title: "#{stage.titleize}" }
= stage.titleize
- %th Duration
+ %th
%th
= render @pipelines, commit_sha: true, stage: true, allow_retry: true, stages: stages
diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml
index 6c994ae486b..1646bcf4b8a 100644
--- a/app/views/projects/snippets/index.html.haml
+++ b/app/views/projects/snippets/index.html.haml
@@ -1,6 +1,6 @@
- page_title "Snippets"
-.row-content-block.top-block
+.sub-header-block
.pull-right
- if can?(current_user, :create_project_snippet, @project)
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do
diff --git a/app/views/shared/_import_form.html.haml b/app/views/shared/_import_form.html.haml
index 627814bcfae..65a3a6bddab 100644
--- a/app/views/shared/_import_form.html.haml
+++ b/app/views/shared/_import_form.html.haml
@@ -2,7 +2,7 @@
= f.label :import_url, class: 'control-label' do
%span Git repository URL
.col-sm-10
- = f.text_field :import_url, class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git'
+ = f.text_field :import_url, class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git', disabled: true
.well.prepend-top-20
%ul
diff --git a/app/views/shared/icons/_group.svg b/app/views/shared/icons/_group.svg.erb
index 75cae0d16c8..53635016900 100644
--- a/app/views/shared/icons/_group.svg
+++ b/app/views/shared/icons/_group.svg.erb
@@ -1,9 +1,4 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
- <!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch -->
- <title>Group</title>
- <desc>Created with Sketch.</desc>
- <defs></defs>
+<svg width="<%= size %>" height="<%= size %>" viewBox="0 0 16 16">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Group" fill="#303030">
<path d="M15.6667,10.0105 L10.3337,10.0105 C10.1497,10.0105 9.9997,10.1775 9.9997,10.3845 L9.9997,15.6145 C9.9997,15.8215 10.1497,15.9885 10.3337,15.9885 L15.6667,15.9885 C15.8507,15.9885 15.9997,15.8215 15.9997,15.6145 L15.9997,10.3845 C15.9997,10.1775 15.8507,10.0105 15.6667,10.0105 L15.6667,10.0105 L15.6667,10.0105 Z M11.9997,14.0105 L13.9997,14.0105 L13.9997,12.0105 L11.9997,12.0105 L11.9997,14.0105 L11.9997,14.0105 Z" id="Fill-11"></path>
@@ -15,4 +10,4 @@
<path d="M11.6667,6.21724894e-15 L4.3337,6.21724894e-15 C4.1497,6.21724894e-15 3.9997,0.167 3.9997,0.374 L3.9997,6.604 C3.9997,6.811 4.1497,6.978 4.3337,6.978 L11.6667,6.978 C11.8507,6.978 11.9997,6.811 11.9997,6.604 L11.9997,0.374 C11.9997,0.167 11.8507,6.21724894e-15 11.6667,6.21724894e-15 L11.6667,6.21724894e-15 L11.6667,6.21724894e-15 Z M5.9997,5 L9.9997,5 L9.9997,2 L5.9997,2 L5.9997,5 L5.9997,5 Z" id="Fill-14"></path>
</g>
</g>
-</svg> \ No newline at end of file
+</svg>
diff --git a/app/views/shared/icons/_icon_commit.svg b/app/views/shared/icons/_icon_commit.svg
new file mode 100644
index 00000000000..0e96035b7b7
--- /dev/null
+++ b/app/views/shared/icons/_icon_commit.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
+ <path fill="#8F8F8F" fill-rule="evenodd" d="M28.7769836,18 C27.8675252,13.9920226 24.2831748,11 20,11 C15.7168252,11 12.1324748,13.9920226 11.2230164,18 L4.0085302,18 C2.90195036,18 2,18.8954305 2,20 C2,21.1122704 2.8992496,22 4.0085302,22 L11.2230164,22 C12.1324748,26.0079774 15.7168252,29 20,29 C24.2831748,29 27.8675252,26.0079774 28.7769836,22 L35.9914698,22 C37.0980496,22 38,21.1045695 38,20 C38,18.8877296 37.1007504,18 35.9914698,18 L28.7769836,18 L28.7769836,18 Z M20,25 C22.7614237,25 25,22.7614237 25,20 C25,17.2385763 22.7614237,15 20,15 C17.2385763,15 15,17.2385763 15,20 C15,22.7614237 17.2385763,25 20,25 L20,25 Z"/>
+</svg>
diff --git a/app/views/shared/icons/_icon_timer.svg b/app/views/shared/icons/_icon_timer.svg
new file mode 100644
index 00000000000..0b1e5804427
--- /dev/null
+++ b/app/views/shared/icons/_icon_timer.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><g fill="#8F8F8F" fill-rule="evenodd"><path d="M29.513 10.134A15.922 15.922 0 0 0 23 7.28V6h2.993C26.55 6 27 5.552 27 5V2a1 1 0 0 0-1.007-1H14.007C13.45 1 13 1.448 13 2v3a1 1 0 0 0 1.007 1H17v1.28C9.597 8.686 4 15.19 4 23c0 8.837 7.163 16 16 16s16-7.163 16-16c0-3.461-1.099-6.665-2.967-9.283l1.327-1.58a2.498 2.498 0 0 0-.303-3.53 2.499 2.499 0 0 0-3.528.315l-1.016 1.212zM20 34c6.075 0 11-4.925 11-11s-4.925-11-11-11S9 16.925 9 23s4.925 11 11 11z"/><path d="M19 21h-4.002c-.552 0-.998.452-.998 1.01v1.98c0 .567.447 1.01.998 1.01h7.004c.274 0 .521-.111.701-.291a.979.979 0 0 0 .297-.704v-8.01c0-.54-.452-.995-1.01-.995h-1.98a.997.997 0 0 0-1.01.995V21z"/></g></svg> \ No newline at end of file
diff --git a/app/views/shared/icons/_project.svg b/app/views/shared/icons/_project.svg
deleted file mode 100644
index 1e8b43f8c6b..00000000000
--- a/app/views/shared/icons/_project.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
- <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
- <title>Page 1</title>
- <desc>Created with Sketch.</desc>
- <defs></defs>
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
- <path d="M6,6 L12,6 L12,5 L6,5 L6,6 Z M6,8 L12,8 L12,7 L6,7 L6,8 Z M6,10 L12,10 L12,9 L6,9 L6,10 Z M6,12 L12,12 L12,11 L6,11 L6,12 Z M4,6 L5,6 L5,5 L4,5 L4,6 Z M4,8 L5,8 L5,7 L4,7 L4,8 Z M4,10 L5,10 L5,9 L4,9 L4,10 Z M4,12 L5,12 L5,11 L4,11 L4,12 Z M13,3 L10,3 L10,4 L6,4 L6,3 L3,3 L3,13 L13,13 L13,3 Z M2,14 L14,14 L14,2 L2,2 L2,14 Z M1,0 C0.448,0 0,0.448 0,1 L0,15 C0,15.552 0.448,16 1,16 L15,16 C15.552,16 16,15.552 16,15 L16,1 C16,0.448 15.552,0 15,0 L1,0 Z" fill="#7F7E7E"></path>
- </g>
-</svg> \ No newline at end of file
diff --git a/app/views/shared/icons/_project.svg.erb b/app/views/shared/icons/_project.svg.erb
new file mode 100644
index 00000000000..2f60bb7245e
--- /dev/null
+++ b/app/views/shared/icons/_project.svg.erb
@@ -0,0 +1,3 @@
+<svg width="<%= size %>" height="<%= size %>" viewBox="0 0 16 16">
+ <path d="M6,6 L12,6 L12,5 L6,5 L6,6 Z M6,8 L12,8 L12,7 L6,7 L6,8 Z M6,10 L12,10 L12,9 L6,9 L6,10 Z M6,12 L12,12 L12,11 L6,11 L6,12 Z M4,6 L5,6 L5,5 L4,5 L4,6 Z M4,8 L5,8 L5,7 L4,7 L4,8 Z M4,10 L5,10 L5,9 L4,9 L4,10 Z M4,12 L5,12 L5,11 L4,11 L4,12 Z M13,3 L10,3 L10,4 L6,4 L6,3 L3,3 L3,13 L13,13 L13,3 Z M2,14 L14,14 L14,2 L2,2 L2,14 Z M1,0 C0.448,0 0,0.448 0,1 L0,15 C0,15.552 0.448,16 1,16 L15,16 C15.552,16 16,15.552 16,15 L16,1 C16,0.448 15.552,0 15,0 L1,0 Z" fill="#7F7E7E" fill-rule="evenodd"></path>
+</svg>
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index 094d6636c66..d5199bd86dd 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -21,10 +21,10 @@
placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (@project.id if @project), selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } })
.filter-item.inline.milestone-filter
- = render "shared/issuable/milestone_dropdown"
+ = render "shared/issuable/milestone_dropdown", selected: params[:milestone_title], name: :milestone_title, show_any: true, show_upcoming: true
.filter-item.inline.labels-filter
- = render "shared/issuable/label_dropdown"
+ = render "shared/issuable/label_dropdown", selected: params[:label_name], data_options: { field_name: "label_name[]" }
.pull-right
= render 'shared/sort_dropdown'
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index 98bbb12eaec..07b39c233b1 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -59,38 +59,24 @@
= f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) }
.issuable-form-select-holder
- = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
- placeholder: 'Select assignee', class: 'custom-form-control', null_user: true,
- selected: issuable.assignee_id, project: @target_project || @project,
- first_user: true, current_user: true, include_blank: true)
- %div
- = link_to 'Assign to me', '#', class: 'assign-to-me-link prepend-top-5 inline'
+ - project = @target_project || @project
+ - if issuable.assignee_id
+ = hidden_field_tag("#{issuable.class.model_name.param_key}[assignee_id]", issuable.assignee_id)
+ = dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-user-search js-issuable-form-dropdown js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
+ placeholder: "Search assignee", data: { first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (project.id if project), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee" } })
.form-group.issue-milestone
= f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) }
- - if milestone_options(issuable).present?
- .issuable-form-select-holder
- = f.select(:milestone_id, milestone_options(issuable),
- { include_blank: true }, { class: 'select2', data: { placeholder: 'Select milestone' } })
- - else
- .prepend-top-10
- %span.light No open milestones available.
- - if can? current_user, :admin_milestone, issuable.project
- %div
- = link_to 'Create new milestone', new_namespace_project_milestone_path(issuable.project.namespace, issuable.project), target: :blank, class: "prepend-top-5 inline"
+ .issuable-form-select-holder
+ = render "shared/issuable/milestone_dropdown", selected: issuable.milestone_id, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false
.form-group
- has_labels = issuable.project.labels.any?
+ - selected_labels = issuable.label_ids.any? ? issuable.label_ids : nil
+ - label_dropdown_toggle = issuable.labels.map { |label| label.title }
= f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
- - if has_labels
- .issuable-form-select-holder
- = f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
- { selected: issuable.label_ids }, multiple: true, class: 'select2', data: { placeholder: "Select labels" }
- - else
- %span.light No labels yet.
- - if can? current_user, :admin_label, issuable.project
- %div
- = link_to 'Create new label', new_namespace_project_label_path(issuable.project.namespace, issuable.project), target: :blank, class: "prepend-top-5 inline"
+ .issuable-form-select-holder
+ = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: selected_labels, selected_toggle: label_dropdown_toggle, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: "false" }
- if has_due_date
.col-lg-6
.form-group
diff --git a/app/views/shared/issuable/_label_dropdown.html.haml b/app/views/shared/issuable/_label_dropdown.html.haml
index d34d28f6736..bcbe133ce62 100644
--- a/app/views/shared/issuable/_label_dropdown.html.haml
+++ b/app/views/shared/issuable/_label_dropdown.html.haml
@@ -4,19 +4,21 @@
- show_footer = local_assigns.fetch(:show_footer, true)
- data_options = local_assigns.fetch(:data_options, {})
- classes = local_assigns.fetch(:classes, [])
-- dropdown_data = {toggle: 'dropdown', field_name: 'label_name[]', show_no: "true", show_any: "true", selected: params[:label_name], project_id: @project.try(:id), labels: labels_filter_path, default_label: "Label"}
+- selected = local_assigns.fetch(:selected, nil)
+- selected_toggle = local_assigns.fetch(:selected_toggle, nil)
+- dropdown_data = {toggle: 'dropdown', field_name: "label_name[]", show_no: "true", show_any: "true", selected: selected, project_id: @project.try(:id), labels: labels_filter_path, default_label: "Label"}
- dropdown_data.merge!(data_options)
- classes << 'js-extra-options' if extra_options
- classes << 'js-filter-submit' if filter_submit
-- if params[:label_name].present?
- - if params[:label_name].respond_to?('any?')
- - params[:label_name].each do |label|
- = hidden_field_tag "label_name[]", label, id: nil
+- if selected.present?
+ - if selected.respond_to?('any?')
+ - selected.each do |label|
+ = hidden_field_tag data_options[:field_name], label, id: nil
.dropdown
%button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data}
%span.dropdown-toggle-text
- = h(multi_label_name(params[:label_name], "Label"))
+ = h(multi_label_name(selected_toggle || selected, "Label"))
= icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
= render partial: "shared/issuable/label_page_default", locals: { title: "Filter by label", show_footer: show_footer, show_create: show_create }
diff --git a/app/views/shared/issuable/_milestone_dropdown.html.haml b/app/views/shared/issuable/_milestone_dropdown.html.haml
index 2fcf40ece99..9188ef72d52 100644
--- a/app/views/shared/issuable/_milestone_dropdown.html.haml
+++ b/app/views/shared/issuable/_milestone_dropdown.html.haml
@@ -1,7 +1,7 @@
-- if params[:milestone_title].present?
- = hidden_field_tag(:milestone_title, params[:milestone_title])
-= dropdown_tag(milestone_dropdown_label(params[:milestone_title]), options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit', filter: true, dropdown_class: "dropdown-menu-selectable",
- placeholder: "Search milestones", footer_content: @project.present?, data: { show_no: true, show_any: true, show_upcoming: true, field_name: "milestone_title", selected: params[:milestone_title], project_id: @project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
+- if selected.present?
+ = hidden_field_tag(name, selected)
+= dropdown_tag(milestone_dropdown_label(selected), options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit', filter: true, dropdown_class: "dropdown-menu-selectable",
+ placeholder: "Search milestones", footer_content: @project.present?, data: { show_no: true, show_any: show_any, show_upcoming: show_upcoming, field_name: name, selected: selected, project_id: @project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
- if @project
%ul.dropdown-footer-list
- if can? current_user, :admin_milestone, @project
diff --git a/config/gitlab.teatro.yml b/config/gitlab.teatro.yml
deleted file mode 100644
index 75b79b837e0..00000000000
--- a/config/gitlab.teatro.yml
+++ /dev/null
@@ -1,87 +0,0 @@
-
-production: &base
- gitlab:
- host: localhost
- port: 80
- https: false
-
- user: root
-
- email_from: example@example.com
-
- support_email: support@example.com
-
- default_projects_features:
- issues: true
- merge_requests: true
- wiki: true
- snippets: false
- visibility_level: "private" # can be "private" | "internal" | "public"
-
- issues_tracker:
-
- gravatar:
- enabled: true # Use user avatar image from Gravatar.com (default: true)
-
- ldap:
- enabled: false
- host: '_your_ldap_server'
- port: 636
- uid: 'sAMAccountName'
- method: 'ssl' # "tls" or "ssl" or "plain"
- bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
- password: '_the_password_of_the_bind_user'
- allow_username_or_email_login: true
-
- base: ''
-
- user_filter: ''
-
- omniauth:
- enabled: false
-
- satellites:
- # Relative paths are relative to Rails.root (default: tmp/repo_satellites/)
- path: /apps/gitlab-satellites/
-
- backup:
- path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/)
-
- repositories:
- storages: # REPO PATHS MUST NOT BE A SYMLINK!!!
- default: /apps/repositories/
-
- gitlab_shell:
- path: /apps/gitlab-shell/
-
- hooks_path: /apps/gitlab-shell/hooks/
-
- upload_pack: true
- receive_pack: true
-
- git:
- bin_path: /usr/bin/git
- max_size: 5242880 # 5.megabytes
- timeout: 10
-
- extra:
-
-development:
- <<: *base
-
-test:
- <<: *base
- gravatar:
- enabled: true
- gitlab:
- host: localhost
- port: 80
- issues_tracker:
- redmine:
- title: "Redmine"
- project_url: "http://redmine/projects/:issues_tracker_id"
- issues_url: "http://redmine/:project_id/:issues_tracker_id/:id"
- new_issue_url: "http://redmine/projects/:issues_tracker_id/issues/new"
-
-staging:
- <<: *base
diff --git a/db/migrate/20160712171823_remove_award_emojis_with_no_user.rb b/db/migrate/20160712171823_remove_award_emojis_with_no_user.rb
new file mode 100644
index 00000000000..668c22bb51c
--- /dev/null
+++ b/db/migrate/20160712171823_remove_award_emojis_with_no_user.rb
@@ -0,0 +1,21 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class RemoveAwardEmojisWithNoUser < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # When using the methods "add_concurrent_index" or "add_column_with_default"
+ # you must disable the use of transactions as these methods can not run in an
+ # existing transaction. When using "add_concurrent_index" make sure that this
+ # method is the _only_ method called in the migration, any other changes
+ # should go in a separate migration. This ensures that upon failure _only_ the
+ # index creation fails and can be retried or reverted easily.
+ #
+ # To disable transactions uncomment the following line and remove these
+ # comments:
+ # disable_ddl_transaction!
+
+ def up
+ AwardEmoji.joins('LEFT JOIN users ON users.id = user_id').where('users.id IS NULL').destroy_all
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 9d31947d80f..f24e47b85b2 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: 20160707104333) do
+ActiveRecord::Schema.define(version: 20160712171823) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
diff --git a/doc/api/todos.md b/doc/api/todos.md
index 29e73664410..23f6e35f2a4 100644
--- a/doc/api/todos.md
+++ b/doc/api/todos.md
@@ -15,7 +15,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `action` | string | no | The action to be filtered. Can be `assigned`, `mentioned`, `build_failed`, or `marked`. |
+| `action` | string | no | The action to be filtered. Can be `assigned`, `mentioned`, `build_failed`, `marked`, or `approval_required`. |
| `author_id` | integer | no | The ID of an author |
| `project_id` | integer | no | The ID of a project |
| `state` | string | no | The state of the todo. Can be either `pending` or `done` |
diff --git a/doc/development/ui_guide.md b/doc/development/ui_guide.md
index 8d02c52c477..65252288019 100644
--- a/doc/development/ui_guide.md
+++ b/doc/development/ui_guide.md
@@ -1,45 +1,45 @@
-# UI Guide for building GitLab
+# UI Guide for building GitLab
## GitLab UI development kit
We created a page inside GitLab where you can check commonly used html and css elements.
-When you run GitLab instance locally - just visit http://localhost:3000/help/ui page to see UI examples
+When you run GitLab instance locally - just visit http://localhost:3000/help/ui page to see UI examples
you can use during GitLab development.
## Design repository
-All design files are stored in the [gitlab-design](https://gitlab.com/gitlab-org/gitlab-design)
-repository and maintained by GitLab UX designers.
+All design files are stored in the [gitlab-design](https://gitlab.com/gitlab-org/gitlab-design)
+repository and maintained by GitLab UX designers.
## Navigation
-GitLab's layout contains 2 sections: the left sidebar and the content. The left sidebar contains a static navigation menu.
-This menu will be visible regardless of what page you visit. The left sidebar also contains the GitLab logo
-and the current user's profile picture. The content section contains a header and the content itself.
-The header describes the current GitLab page and what navigation is
-available to user in this area. Depending on the area (project, group, profile setting) the header name and navigation may change. For example when user visits one of the
+GitLab's layout contains 2 sections: the left sidebar and the content. The left sidebar contains a static navigation menu.
+This menu will be visible regardless of what page you visit. The left sidebar also contains the GitLab logo
+and the current user's profile picture. The content section contains a header and the content itself.
+The header describes the current GitLab page and what navigation is
+available to user in this area. Depending on the area (project, group, profile setting) the header name and navigation may change. For example when user visits one of the
project pages the header will contain a project name and navigation for that project. When the user visits a group page it will contain a group name and navigation related to this group.
### Adding new tab to header navigation
-We try to keep the amount of tabs in the header navigation between 5 and 10 so that it fits on a typical laptop screen. We also try not to confuse the user with too many options. Ideally each
-tab should represent separate functionality. Everything related to the issue
-tracker should be under the 'Issues' tab while everything related to the wiki should
+We try to keep the amount of tabs in the header navigation between 5 and 10 so that it fits on a typical laptop screen. We also try not to confuse the user with too many options. Ideally each
+tab should represent separate functionality. Everything related to the issue
+tracker should be under the 'Issues' tab while everything related to the wiki should
be under 'Wiki' tab and so on and so forth.
-When adding a new tab to the header don't use more than 2 words for text in the link.
+When adding a new tab to the header don't use more than 2 words for text in the link.
We want to keep links short and easy to remember and fit all of them in the small screen.
-## Mobile screen size
+## Mobile screen size
-We want GitLab to work well on small mobile screens as well. Size limitations make it is impossible to fit everything on a mobile screen. In this case it is OK to hide
-part of the UI for smaller resolutions in favor of a better user experience.
+We want GitLab to work well on small mobile screens as well. Size limitations make it is impossible to fit everything on a mobile screen. In this case it is OK to hide
+part of the UI for smaller resolutions in favor of a better user experience.
However core functionality like browsing files, creating issues, writing comments, should
be available on all resolutions.
## Icons
-* `trash` icon for button or link that does destructive action like removing
+* `trash` icon for button or link that does destructive action like removing
information from database or file system
* `x` icon for closing/hiding UI element. For example close modal window
* `pencil` icon for edit button or link
@@ -52,8 +52,14 @@ information from database or file system
* Button should contain icon or text. Exceptions should be approved by UX designer.
* Use red button for destructive actions (not revertable). For example removing issue.
-* Use green or blue button for primary action. Primary button should be only one.
-Do not use both green and blue button in one form.
-* For all other cases use default white button.
-* Text button should have only first word capitalized. So should be "Create issue" instead of "Create Issue"
+* Use green or blue button for primary action. Primary button should be only one.
+Do not use both green and blue button in one form.
+* For all other cases use default white button.
+* Text button should have only first word capitalized. So should be "Create issue" instead of "Create Issue"
+## Counts
+
+* Always use the [`number_with_delimiter`][number_with_delimiter] helper to
+ display counts in the UI.
+
+[number_with_delimiter]: http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_with_delimiter
diff --git a/doc/integration/oauth_provider.md b/doc/integration/oauth_provider.md
index 5f8bb57365c..0c53584d201 100644
--- a/doc/integration/oauth_provider.md
+++ b/doc/integration/oauth_provider.md
@@ -28,7 +28,8 @@ GitLab supports two ways of adding a new OAuth2 application to an instance. You
can either add an application as a regular user or add it in the admin area.
What this means is that GitLab can actually have instance-wide and a user-wide
applications. There is no difference between them except for the different
-permission levels they are set (user/admin).
+permission levels they are set (user/admin). The default callback URL is
+`http://your-gitlab.example.com/users/auth/gitlab/callback`
## Adding an application through the profile
diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature
index 358e622b736..80670063ea0 100644
--- a/features/project/issues/issues.feature
+++ b/features/project/issues/issues.feature
@@ -37,6 +37,7 @@ Feature: Project Issues
And I submit new issue "500 error on profile"
Then I should see issue "500 error on profile"
+ @javascript
Scenario: I submit new unassigned issue with labels
Given project "Shop" has labels: "bug", "feature", "enhancement"
And I click link "New Issue"
diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb
index 6b56a77b832..8f71dfdd899 100644
--- a/features/steps/project/forked_merge_requests.rb
+++ b/features/steps/project/forked_merge_requests.rb
@@ -135,19 +135,17 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
end
step 'I click "Assign to" dropdown"' do
- first('.ajax-users-select').click
+ click_button 'Assignee'
end
step 'I should see the target project ID in the input selector' do
- expect(page).to have_selector("input[data-project-id=\"#{@project.id}\"]")
+ expect(find('.js-assignee-search')["data-project-id"]).to eq "#{@project.id}"
end
step 'I should see the users from the target project ID' do
- expect(page).to have_selector('.user-result', visible: true, count: 3)
- users = page.all('.user-name')
- expect(users[0].text).to eq 'Unassigned'
- expect(users[1].text).to eq current_user.name
- expect(users[2].text).to eq @project.users.first.name
+ expect(page).to have_content 'Unassigned'
+ expect(page).to have_content current_user.name
+ expect(page).to have_content @project.users.first.name
end
# Verify a link is generated against the correct project
diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb
index 35f166c7c08..b785e15f70e 100644
--- a/features/steps/project/issues/issues.rb
+++ b/features/steps/project/issues/issues.rb
@@ -82,7 +82,8 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
step 'I submit new issue "500 error on profile" with label \'bug\'' do
fill_in "issue_title", with: "500 error on profile"
- select 'bug', from: "Labels"
+ click_button "Label"
+ click_link "bug"
click_button "Submit issue"
end
diff --git a/features/support/env.rb b/features/support/env.rb
index ab3f0ca7aeb..f0a3dd8d2d0 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -13,7 +13,7 @@ require_relative 'rerun'
if ENV['CI']
require 'knapsack'
- Knapsack::Adapters::RSpecAdapter.bind
+ Knapsack::Adapters::SpinachAdapter.bind
end
%w(select2_helper test_env repo_helpers).each do |f|
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 301dbb688a7..8e03c08f47b 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -54,6 +54,7 @@ module API
class BasicProjectDetails < Grape::Entity
expose :id
+ expose :http_url_to_repo, :web_url
expose :name, :name_with_namespace
expose :path, :path_with_namespace
end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 0cc1edd65c8..6d2a6f3946c 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -25,7 +25,11 @@ module API
@projects = current_user.authorized_projects
@projects = filter_projects(@projects)
@projects = paginate @projects
- present @projects, with: Entities::ProjectWithAccess, user: current_user
+ if params[:simple]
+ present @projects, with: Entities::BasicProjectDetails, user: current_user
+ else
+ present @projects, with: Entities::ProjectWithAccess, user: current_user
+ end
end
# Get an owned projects list for authenticated user
diff --git a/lib/banzai/renderer.rb b/lib/banzai/renderer.rb
index ea10b06439b..910687a7b6a 100644
--- a/lib/banzai/renderer.rb
+++ b/lib/banzai/renderer.rb
@@ -61,10 +61,16 @@ module Banzai
end
cacheable_items, non_cacheable_items = items_collection.partition { |item| item.key?(:cache_key) }
- items_in_cache = Rails.cache.read_multi(*cacheable_items.map { |item| item[:cache_key] })
- items_not_in_cache = cacheable_items.reject do |item|
- item[:rendered] = items_in_cache[item[:cache_key]]
- items_in_cache.key?(item[:cache_key])
+
+ items_in_cache = []
+ items_not_in_cache = []
+
+ unless cacheable_items.empty?
+ items_in_cache = Rails.cache.read_multi(*cacheable_items.map { |item| item[:cache_key] })
+ items_not_in_cache = cacheable_items.reject do |item|
+ item[:rendered] = items_in_cache[item[:cache_key]]
+ items_in_cache.key?(item[:cache_key])
+ end
end
(items_not_in_cache + non_cacheable_items).each do |item|
diff --git a/lib/gitlab/diff/inline_diff.rb b/lib/gitlab/diff/inline_diff.rb
index 789c14518b0..28ad637fda4 100644
--- a/lib/gitlab/diff/inline_diff.rb
+++ b/lib/gitlab/diff/inline_diff.rb
@@ -1,16 +1,30 @@
module Gitlab
module Diff
class InlineDiff
+ # Regex to find a run of deleted lines followed by the same number of added lines
+ LINE_PAIRS_PATTERN = %r{
+ # Runs start at the beginning of the string (the first line) or after a space (for an unchanged line)
+ (?:\A|\s)
+
+ # This matches a number of `-`s followed by the same number of `+`s through recursion
+ (?<del_ins>
+ -
+ \g<del_ins>?
+ \+
+ )
+
+ # Runs end at the end of the string (the last line) or before a space (for an unchanged line)
+ (?=\s|\z)
+ }x.freeze
+
attr_accessor :old_line, :new_line, :offset
def self.for_lines(lines)
- local_edit_indexes = self.find_local_edits(lines)
+ changed_line_pairs = self.find_changed_line_pairs(lines)
inline_diffs = []
- local_edit_indexes.each do |index|
- old_index = index
- new_index = index + 1
+ changed_line_pairs.each do |old_index, new_index|
old_line = lines[old_index]
new_line = lines[new_index]
@@ -51,18 +65,28 @@ module Gitlab
private
- def self.find_local_edits(lines)
- line_prefixes = lines.map { |line| line.match(/\A([+-])/) ? $1 : ' ' }
- joined_line_prefixes = " #{line_prefixes.join} "
-
- offset = 0
- local_edit_indexes = []
- while index = joined_line_prefixes.index(" -+ ", offset)
- local_edit_indexes << index
- offset = index + 1
+ # Finds pairs of old/new line pairs that represent the same line that changed
+ def self.find_changed_line_pairs(lines)
+ # Prefixes of all diff lines, indicating their types
+ # For example: `" - + -+ ---+++ --+ -++"`
+ line_prefixes = lines.each_with_object("") { |line, s| s << line[0] }.gsub(/[^ +-]/, ' ')
+
+ changed_line_pairs = []
+ line_prefixes.scan(LINE_PAIRS_PATTERN) do
+ # For `"---+++"`, `begin_index == 0`, `end_index == 6`
+ begin_index, end_index = Regexp.last_match.offset(:del_ins)
+
+ # For `"---+++"`, `changed_line_count == 3`
+ changed_line_count = (end_index - begin_index) / 2
+
+ halfway_index = begin_index + changed_line_count
+ (begin_index...halfway_index).each do |i|
+ # For `"---+++"`, index 1 maps to 1 + 3 = 4
+ changed_line_pairs << [i, i + changed_line_count]
+ end
end
- local_edit_indexes
+ changed_line_pairs
end
def longest_common_prefix(a, b)
diff --git a/lib/gitlab/diff/parallel_diff.rb b/lib/gitlab/diff/parallel_diff.rb
index 1c1fc148123..b069afdd28c 100644
--- a/lib/gitlab/diff/parallel_diff.rb
+++ b/lib/gitlab/diff/parallel_diff.rb
@@ -8,95 +8,78 @@ module Gitlab
end
def parallelize
- lines = []
- skip_next = false
+ i = 0
+ free_right_index = nil
+
+ lines = []
highlighted_diff_lines = diff_file.highlighted_diff_lines
highlighted_diff_lines.each do |line|
- full_line = line.text
- type = line.type
line_code = diff_file.line_code(line)
- line_new = line.new_pos
- line_old = line.old_pos
position = diff_file.position(line)
- next_line = diff_file.next_line(line.index)
-
- if next_line
- next_line = highlighted_diff_lines[next_line.index]
- full_next_line = next_line.text
- next_line_code = diff_file.line_code(next_line)
- next_type = next_line.type
- next_position = diff_file.position(next_line)
- end
-
- case type
+ case line.type
when 'match', nil
# line in the right panel is the same as in the left one
lines << {
left: {
- type: type,
- number: line_old,
- text: full_line,
+ type: line.type,
+ number: line.old_pos,
+ text: line.text,
line_code: line_code,
position: position
},
right: {
- type: type,
- number: line_new,
- text: full_line,
+ type: line.type,
+ number: line.new_pos,
+ text: line.text,
line_code: line_code,
position: position
}
}
+
+ free_right_index = nil
+ i += 1
when 'old'
- case next_type
- when 'new'
- # Left side has text removed, right side has text added
- lines << {
- left: {
- type: type,
- number: line_old,
- text: full_line,
- line_code: line_code,
- position: position
- },
- right: {
- type: next_type,
- number: line_new,
- text: full_next_line,
- line_code: next_line_code,
- position: next_position,
- }
- }
- skip_next = true
- when 'old', 'nonewline', nil
- # Left side has text removed, right side doesn't have any change
- # No next line code, no new line number, no new line text
- lines << {
- left: {
- type: type,
- number: line_old,
- text: full_line,
- line_code: line_code,
- position: position
- },
- right: {
- type: next_type,
- number: nil,
- text: "",
- line_code: nil,
- position: nil
- }
+ lines << {
+ left: {
+ type: line.type,
+ number: line.old_pos,
+ text: line.text,
+ line_code: line_code,
+ position: position
+ },
+ right: {
+ type: nil,
+ number: nil,
+ text: "",
+ line_code: line_code,
+ position: position
}
- end
+ }
+
+ # Once we come upon a new line it can be put on the right of this old line
+ free_right_index ||= i
+ i += 1
when 'new'
- if skip_next
- # Change has been already included in previous line so no need to do it again
- skip_next = false
- next
+ data = {
+ type: line.type,
+ number: line.new_pos,
+ text: line.text,
+ line_code: line_code,
+ position: position
+ }
+
+ if free_right_index
+ # If an old line came before this without a line on the right, this
+ # line can be put to the right of it.
+ lines[free_right_index][:right] = data
+
+ # If there are any other old lines on the left that don't yet have
+ # a new counterpart on the right, update the free_right_index
+ next_free_right_index = free_right_index + 1
+ free_right_index = next_free_right_index < i ? next_free_right_index : nil
else
- # Change is only on the right side, left side has no change
lines << {
left: {
type: nil,
@@ -105,17 +88,15 @@ module Gitlab
line_code: line_code,
position: position
},
- right: {
- type: type,
- number: line_new,
- text: full_line,
- line_code: line_code,
- position: position
- }
+ right: data
}
+
+ free_right_index = nil
+ i += 1
end
end
end
+
lines
end
end
diff --git a/lib/gitlab/gitlab_import/importer.rb b/lib/gitlab/gitlab_import/importer.rb
index 3f76ec97977..e6d31ea04c0 100644
--- a/lib/gitlab/gitlab_import/importer.rb
+++ b/lib/gitlab/gitlab_import/importer.rb
@@ -35,6 +35,7 @@ module Gitlab
end
project.issues.create!(
+ iid: issue["iid"],
description: body,
title: issue["title"],
state: issue["state"],
diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab
index d521de28e8a..4a4892a2e07 100644
--- a/lib/support/nginx/gitlab
+++ b/lib/support/nginx/gitlab
@@ -49,7 +49,12 @@ server {
proxy_http_version 1.1;
- proxy_set_header Host $http_host;
+ ## By overwriting Host and clearing X-Forwarded-Host we ensure that
+ ## internal HTTP redirects generated by GitLab always send users to
+ ## YOUR_SERVER_FQDN.
+ proxy_set_header Host YOUR_SERVER_FQDN;
+ proxy_set_header X-Forwarded-Host "";
+
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl
index bf014b56cf6..0b93d7f292f 100644
--- a/lib/support/nginx/gitlab-ssl
+++ b/lib/support/nginx/gitlab-ssl
@@ -93,7 +93,12 @@ server {
proxy_http_version 1.1;
- proxy_set_header Host $http_host;
+ ## By overwriting Host and clearing X-Forwarded-Host we ensure that
+ ## internal HTTP redirects generated by GitLab always send users to
+ ## YOUR_SERVER_FQDN.
+ proxy_set_header Host YOUR_SERVER_FQDN;
+ proxy_set_header X-Forwarded-Host "";
+
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb
index 7fc20cd5555..866e663f026 100644
--- a/spec/factories/todos.rb
+++ b/spec/factories/todos.rb
@@ -23,6 +23,10 @@ FactoryGirl.define do
action { Todo::BUILD_FAILED }
end
+ trait :approval_required do
+ action { Todo::APPROVAL_REQUIRED }
+ end
+
trait :done do
state :done
end
diff --git a/spec/features/compare_spec.rb b/spec/features/compare_spec.rb
new file mode 100644
index 00000000000..c62556948e0
--- /dev/null
+++ b/spec/features/compare_spec.rb
@@ -0,0 +1,42 @@
+require "spec_helper"
+
+describe "Compare", js: true do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+
+ before do
+ project.team << [user, :master]
+ login_as user
+ visit namespace_project_compare_index_path(project.namespace, project, from: "master", to: "master")
+ end
+
+ describe "branches" do
+ it "should pre-populate fields" do
+ expect(page.find_field("from").value).to eq("master")
+ end
+
+ it "should compare branches" do
+ fill_in "from", with: "fea"
+ find("#from").click
+
+ click_link "feature"
+ expect(page.find_field("from").value).to eq("feature")
+
+ click_button "Compare"
+ expect(page).to have_content "Commits"
+ end
+ end
+
+ describe "tags" do
+ it "should compare tags" do
+ fill_in "from", with: "v1.0"
+ find("#from").click
+
+ click_link "v1.0.0"
+ expect(page.find_field("from").value).to eq("v1.0.0")
+
+ click_button "Compare"
+ expect(page).to have_content "Commits"
+ end
+ end
+end
diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb
index 7773c486b4e..055210399a7 100644
--- a/spec/features/issues/move_spec.rb
+++ b/spec/features/issues/move_spec.rb
@@ -55,7 +55,7 @@ feature 'issue move to another project' do
first('.select2-choice').click
end
- fill_in('s2id_autogen2_search', with: new_project_search.name)
+ fill_in('s2id_autogen1_search', with: new_project_search.name)
page.within '.select2-drop' do
expect(page).to have_content(new_project_search.name)
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index cfe6349a1a1..3fec75a07df 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -50,9 +50,8 @@ describe 'Issues', feature: true do
expect(page).to have_content "Assignee #{@user.name}"
- first('#s2id_issue_assignee_id').click
- sleep 2 # wait for ajax stuff to complete
- first('.user-result').click
+ first('.js-user-search').click
+ click_link 'Unassigned'
click_button 'Save changes'
diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb
index 72b5ff231f7..58753ff21f6 100644
--- a/spec/features/login_spec.rb
+++ b/spec/features/login_spec.rb
@@ -28,6 +28,11 @@ feature 'Login', feature: true do
end
describe 'with two-factor authentication' do
+ def enter_code(code)
+ fill_in 'Two-Factor Authentication code', with: code
+ click_button 'Verify code'
+ end
+
context 'with valid username/password' do
let(:user) { create(:user, :two_factor) }
@@ -36,11 +41,6 @@ feature 'Login', feature: true do
expect(page).to have_content('Two-Factor Authentication')
end
- def enter_code(code)
- fill_in 'Two-Factor Authentication code', with: code
- click_button 'Verify code'
- end
-
it 'does not show a "You are already signed in." error message' do
enter_code(user.current_otp)
expect(page).not_to have_content('You are already signed in.')
@@ -108,6 +108,39 @@ feature 'Login', feature: true do
end
end
end
+
+ context 'logging in via OAuth' do
+ def saml_config
+ OpenStruct.new(name: 'saml', label: 'saml', args: {
+ assertion_consumer_service_url: 'https://localhost:3443/users/auth/saml/callback',
+ idp_cert_fingerprint: '26:43:2C:47:AF:F0:6B:D0:07:9C:AD:A3:74:FE:5D:94:5F:4E:9E:52',
+ idp_sso_target_url: 'https://idp.example.com/sso/saml',
+ issuer: 'https://localhost:3443/',
+ name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'
+ })
+ end
+
+ def stub_omniauth_config(messages)
+ Rails.application.env_config['devise.mapping'] = Devise.mappings[:user]
+ Rails.application.routes.disable_clear_and_finalize = true
+ Rails.application.routes.draw do
+ post '/users/auth/saml' => 'omniauth_callbacks#saml'
+ end
+ allow(Gitlab::OAuth::Provider).to receive_messages(providers: [:saml], config_for: saml_config)
+ allow(Gitlab.config.omniauth).to receive_messages(messages)
+ allow_any_instance_of(Object).to receive(:user_omniauth_authorize_path).with('saml').and_return('/users/auth/saml')
+ end
+
+ it 'should show 2FA prompt after OAuth login' do
+ stub_omniauth_config(enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'], providers: [saml_config])
+ user = create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: 'saml')
+ login_via('saml', user, 'my-uid')
+
+ expect(page).to have_content('Two-Factor Authentication')
+ enter_code(user.current_otp)
+ expect(current_path).to eq root_path
+ end
+ end
end
describe 'without two-factor authentication' do
diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb
index 1bd354815e4..8db897b1646 100644
--- a/spec/finders/notes_finder_spec.rb
+++ b/spec/finders/notes_finder_spec.rb
@@ -11,7 +11,7 @@ describe NotesFinder do
project.team << [user, :master]
end
- describe :execute do
+ describe '#execute' do
let(:params) { { target_id: commit.id, target_type: 'commit', last_fetched_at: 1.hour.ago.to_i } }
before do
diff --git a/spec/fixtures/parallel_diff_result.yml b/spec/fixtures/parallel_diff_result.yml
index 7d01183e3ef..333eda1191a 100644
--- a/spec/fixtures/parallel_diff_result.yml
+++ b/spec/fixtures/parallel_diff_result.yml
@@ -253,27 +253,6 @@
:start_sha: 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9
:head_sha: 570e7b2abdd848b95f2f578043fc23bd6f6fd24d
:right:
- :type: old
- :number:
- :text: ''
- :line_code:
- :position:
-- :left:
- :type: old
- :number: 14
- :text: |
- -<span id="LC14" class="line"> <span class="n">options</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">chdir: </span><span class="n">path</span> <span class="p">}</span></span>
- :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_14_13
- :position: !ruby/object:Gitlab::Diff::Position
- attributes:
- :old_path: files/ruby/popen.rb
- :new_path: files/ruby/popen.rb
- :old_line: 14
- :new_line:
- :base_sha: 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9
- :start_sha: 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9
- :head_sha: 570e7b2abdd848b95f2f578043fc23bd6f6fd24d
- :right:
:type: new
:number: 13
:text: |
@@ -289,16 +268,17 @@
:start_sha: 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9
:head_sha: 570e7b2abdd848b95f2f578043fc23bd6f6fd24d
- :left:
- :type:
- :number:
- :text: ''
- :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_14
+ :type: old
+ :number: 14
+ :text: |
+ -<span id="LC14" class="line"> <span class="n">options</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">chdir: </span><span class="n">path</span> <span class="p">}</span></span>
+ :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_14_13
:position: !ruby/object:Gitlab::Diff::Position
attributes:
:old_path: files/ruby/popen.rb
:new_path: files/ruby/popen.rb
- :old_line:
- :new_line: 14
+ :old_line: 14
+ :new_line:
:base_sha: 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9
:start_sha: 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9
:head_sha: 570e7b2abdd848b95f2f578043fc23bd6f6fd24d
diff --git a/spec/javascripts/project_title_spec.js.coffee b/spec/javascripts/project_title_spec.js.coffee
index f0d26fb5446..0244119fa0e 100644
--- a/spec/javascripts/project_title_spec.js.coffee
+++ b/spec/javascripts/project_title_spec.js.coffee
@@ -22,7 +22,7 @@ describe 'Project Title', ->
@projects_data = fixture.load('projects.json')[0]
spyOn(jQuery, 'ajax').and.callFake (req) =>
- expect(req.url).toBe('/api/v3/projects.json')
+ expect(req.url).toBe('/api/v3/projects.json?simple=true')
d = $.Deferred()
d.resolve @projects_data
d.promise()
diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index 9e3d2f5825d..9276a154007 100644
--- a/spec/lib/banzai/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -93,8 +93,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
end
it 'links with adjacent text' do
- doc = reference_filter("Label (#{reference}.)")
- expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\)))
+ doc = reference_filter("Label (#{reference}).")
+ expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\)\.))
end
it 'ignores invalid label names' do
@@ -104,8 +104,32 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
end
end
+ context 'String-based single-word references that begin with a digit' do
+ let(:label) { create(:label, name: '2fa', project: project) }
+ let(:reference) { "#{Label.reference_prefix}#{label.name}" }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).to eq urls.
+ namespace_project_issues_url(project.namespace, project, label_name: label.name)
+ expect(doc.text).to eq 'See 2fa'
+ end
+
+ it 'links with adjacent text' do
+ doc = reference_filter("Label (#{reference}).")
+ expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\)\.))
+ end
+
+ it 'ignores invalid label names' do
+ exp = act = "Label #{Label.reference_prefix}#{label.id}#{label.name.reverse}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
context 'String-based single-word references with special characters' do
- let(:label) { create(:label, name: '?gfm&', project: project) }
+ let(:label) { create(:label, name: '?g.fm&', project: project) }
let(:reference) { "#{Label.reference_prefix}#{label.name}" }
it 'links to a valid reference' do
@@ -113,17 +137,17 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
expect(doc.css('a').first.attr('href')).to eq urls.
namespace_project_issues_url(project.namespace, project, label_name: label.name)
- expect(doc.text).to eq 'See ?gfm&'
+ expect(doc.text).to eq 'See ?g.fm&'
end
it 'links with adjacent text' do
- doc = reference_filter("Label (#{reference}.)")
- expect(doc.to_html).to match(%r(\(<a.+><span.+>\?gfm&amp;</span></a>\.\)))
+ doc = reference_filter("Label (#{reference}).")
+ expect(doc.to_html).to match(%r(\(<a.+><span.+>\?g\.fm&amp;</span></a>\)\.))
end
it 'ignores invalid label names' do
act = "Label #{Label.reference_prefix}#{label.name.reverse}"
- exp = "Label #{Label.reference_prefix}&amp;mfg?"
+ exp = "Label #{Label.reference_prefix}&amp;mf.g?"
expect(reference_filter(act).to_html).to eq exp
end
@@ -153,8 +177,32 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
end
end
+ context 'String-based multi-word references that begin with a digit' do
+ let(:label) { create(:label, name: '2 factor authentication', project: project) }
+ let(:reference) { label.to_reference(format: :name) }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).to eq urls.
+ namespace_project_issues_url(project.namespace, project, label_name: label.name)
+ expect(doc.text).to eq 'See 2 factor authentication'
+ end
+
+ it 'links with adjacent text' do
+ doc = reference_filter("Label (#{reference}.)")
+ expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\)))
+ end
+
+ it 'ignores invalid label names' do
+ exp = act = "Label #{Label.reference_prefix}#{label.id}#{label.name.reverse}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
context 'String-based multi-word references with special characters in quotes' do
- let(:label) { create(:label, name: 'gfm & references?', project: project) }
+ let(:label) { create(:label, name: 'g.fm & references?', project: project) }
let(:reference) { label.to_reference(format: :name) }
it 'links to a valid reference' do
@@ -162,22 +210,62 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
expect(doc.css('a').first.attr('href')).to eq urls.
namespace_project_issues_url(project.namespace, project, label_name: label.name)
- expect(doc.text).to eq 'See gfm & references?'
+ expect(doc.text).to eq 'See g.fm & references?'
end
it 'links with adjacent text' do
doc = reference_filter("Label (#{reference}.)")
- expect(doc.to_html).to match(%r(\(<a.+><span.+>gfm &amp; references\?</span></a>\.\)))
+ expect(doc.to_html).to match(%r(\(<a.+><span.+>g\.fm &amp; references\?</span></a>\.\)))
end
it 'ignores invalid label names' do
act = %(Label #{Label.reference_prefix}"#{label.name.reverse}")
- exp = %(Label #{Label.reference_prefix}"?secnerefer &amp; mfg\")
+ exp = %(Label #{Label.reference_prefix}"?secnerefer &amp; mf.g\")
expect(reference_filter(act).to_html).to eq exp
end
end
+ describe 'consecutive references' do
+ let(:bug) { create(:label, name: 'bug', project: project) }
+ let(:feature_proposal) { create(:label, name: 'feature proposal', project: project) }
+ let(:technical_debt) { create(:label, name: 'technical debt', project: project) }
+
+ let(:bug_reference) { "#{Label.reference_prefix}#{bug.name}" }
+ let(:feature_proposal_reference) { feature_proposal.to_reference(format: :name) }
+ let(:technical_debt_reference) { technical_debt.to_reference(format: :name) }
+
+ context 'separated with a comma' do
+ let(:references) { "#{bug_reference}, #{feature_proposal_reference}, #{technical_debt_reference}" }
+
+ it 'links to valid references' do
+ doc = reference_filter("See #{references}")
+
+ expect(doc.css('a').map { |a| a.attr('href') }).to match_array([
+ urls.namespace_project_issues_url(project.namespace, project, label_name: bug.name),
+ urls.namespace_project_issues_url(project.namespace, project, label_name: feature_proposal.name),
+ urls.namespace_project_issues_url(project.namespace, project, label_name: technical_debt.name)
+ ])
+ expect(doc.text).to eq 'See bug, feature proposal, technical debt'
+ end
+ end
+
+ context 'separated with a space' do
+ let(:references) { "#{bug_reference} #{feature_proposal_reference} #{technical_debt_reference}" }
+
+ it 'links to valid references' do
+ doc = reference_filter("See #{references}")
+
+ expect(doc.css('a').map { |a| a.attr('href') }).to match_array([
+ urls.namespace_project_issues_url(project.namespace, project, label_name: bug.name),
+ urls.namespace_project_issues_url(project.namespace, project, label_name: feature_proposal.name),
+ urls.namespace_project_issues_url(project.namespace, project, label_name: technical_debt.name)
+ ])
+ expect(doc.text).to eq 'See bug feature proposal technical debt'
+ end
+ end
+ end
+
describe 'edge cases' do
it 'gracefully handles non-references matching the pattern' do
exp = act = '(format nil "~0f" 3.0) ; 3.0'
diff --git a/spec/lib/banzai/object_renderer_spec.rb b/spec/lib/banzai/object_renderer_spec.rb
index cf6cdd33ebb..bcdb95250ca 100644
--- a/spec/lib/banzai/object_renderer_spec.rb
+++ b/spec/lib/banzai/object_renderer_spec.rb
@@ -109,6 +109,17 @@ describe Banzai::ObjectRenderer do
expect(docs[1]).to be_an_instance_of(Nokogiri::HTML::DocumentFragment)
expect(docs[1].to_html).to eq('<p>bye</p>')
end
+
+ it 'returns when no objects to render' do
+ objects = []
+ renderer = described_class.new(project, user, pipeline: :note)
+
+ expect(Banzai).to receive(:cache_collection_render).
+ with([]).
+ and_call_original
+
+ expect(renderer.render_attributes(objects, :note)).to eq([])
+ end
end
describe '#base_context' do
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index bad439bc489..bcbf409c8b0 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -31,7 +31,7 @@ module Ci
})
end
- describe :only do
+ describe 'only' do
it "does not return builds if only has another branch" do
config = YAML.dump({
before_script: ["pwd"],
@@ -187,7 +187,7 @@ module Ci
end
end
- describe :except do
+ describe 'except' do
it "returns builds if except has another branch" do
config = YAML.dump({
before_script: ["pwd"],
diff --git a/spec/lib/gitlab/build_data_builder_spec.rb b/spec/lib/gitlab/build_data_builder_spec.rb
index 38be9448794..23ae5cfacc4 100644
--- a/spec/lib/gitlab/build_data_builder_spec.rb
+++ b/spec/lib/gitlab/build_data_builder_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe 'Gitlab::BuildDataBuilder' do
let(:build) { create(:ci_build) }
- describe :build do
+ describe '.build' do
let(:data) do
Gitlab::BuildDataBuilder.build(build)
end
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index 1cb513d5229..0460dcf4658 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -8,14 +8,14 @@ describe Gitlab::Diff::File, lib: true do
let(:diff) { commit.diffs.first }
let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs: commit.diff_refs, repository: project.repository) }
- describe :diff_lines do
+ describe '#diff_lines' do
let(:diff_lines) { diff_file.diff_lines }
it { expect(diff_lines.size).to eq(30) }
it { expect(diff_lines.first).to be_kind_of(Gitlab::Diff::Line) }
end
- describe :mode_changed? do
+ describe '#mode_changed?' do
it { expect(diff_file.mode_changed?).to be_falsey }
end
diff --git a/spec/lib/gitlab/diff/inline_diff_spec.rb b/spec/lib/gitlab/diff/inline_diff_spec.rb
index 95a993d26cf..8ca3f73509e 100644
--- a/spec/lib/gitlab/diff/inline_diff_spec.rb
+++ b/spec/lib/gitlab/diff/inline_diff_spec.rb
@@ -3,14 +3,19 @@ require 'spec_helper'
describe Gitlab::Diff::InlineDiff, lib: true do
describe '.for_lines' do
let(:diff) do
- <<eos
- class Test
-- def initialize(test = true)
-+ def initialize(test = false)
- @test = test
- end
- end
-eos
+ <<-EOF.strip_heredoc
+ class Test
+ - def initialize(test = true)
+ + def initialize(test = false)
+ @test = test
+ - if true
+ - @foo = "bar"
+ + unless false
+ + @foo = "baz"
+ end
+ end
+ end
+ EOF
end
let(:subject) { described_class.for_lines(diff.lines) }
@@ -20,8 +25,11 @@ eos
expect(subject[1]).to eq([25..27])
expect(subject[2]).to eq([25..28])
expect(subject[3]).to be_nil
- expect(subject[4]).to be_nil
- expect(subject[5]).to be_nil
+ expect(subject[4]).to eq([5..10])
+ expect(subject[5]).to eq([17..17])
+ expect(subject[6]).to eq([5..15])
+ expect(subject[7]).to eq([17..17])
+ expect(subject[8]).to be_nil
end
end
diff --git a/spec/lib/gitlab/diff/parser_spec.rb b/spec/lib/gitlab/diff/parser_spec.rb
index cdff063a9ed..c3359627652 100644
--- a/spec/lib/gitlab/diff/parser_spec.rb
+++ b/spec/lib/gitlab/diff/parser_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::Diff::Parser, lib: true do
let(:diff) { commit.diffs.first }
let(:parser) { Gitlab::Diff::Parser.new }
- describe :parse do
+ describe '#parse' do
let(:diff) do
<<eos
--- a/files/ruby/popen.rb
diff --git a/spec/lib/gitlab/gitlab_import/importer_spec.rb b/spec/lib/gitlab/gitlab_import/importer_spec.rb
new file mode 100644
index 00000000000..d3f1deb3837
--- /dev/null
+++ b/spec/lib/gitlab/gitlab_import/importer_spec.rb
@@ -0,0 +1,53 @@
+require 'spec_helper'
+
+describe Gitlab::GitlabImport::Importer, lib: true do
+ include ImportSpecHelper
+
+ describe '#execute' do
+ before do
+ stub_omniauth_provider('gitlab')
+ stub_request('issues', [
+ {
+ 'id' => 2579857,
+ 'iid' => 3,
+ 'title' => 'Issue',
+ 'description' => 'Lorem ipsum',
+ 'state' => 'opened',
+ 'author' => {
+ 'id' => 283999,
+ 'name' => 'John Doe'
+ }
+ }
+ ])
+ stub_request('issues/2579857/notes', [])
+ end
+
+ it 'persists issues' do
+ project = create(:empty_project, import_source: 'asd/vim')
+ project.build_import_data(credentials: { password: 'password' })
+
+ subject = described_class.new(project)
+ subject.execute
+
+ expected_attributes = {
+ iid: 3,
+ title: 'Issue',
+ description: "*Created by: John Doe*\n\nLorem ipsum",
+ state: 'opened',
+ author_id: project.creator_id
+ }
+
+ expect(project.issues.first).to have_attributes(expected_attributes)
+ end
+
+ def stub_request(path, body)
+ url = "https://gitlab.com/api/v3/projects/asd%2Fvim/#{path}?page=1&per_page=100"
+
+ WebMock.stub_request(:get, url).
+ to_return(
+ headers: { 'Content-Type' => 'application/json' },
+ body: body
+ )
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ldap/access_spec.rb b/spec/lib/gitlab/ldap/access_spec.rb
index f5b66b8156f..acd5394382c 100644
--- a/spec/lib/gitlab/ldap/access_spec.rb
+++ b/spec/lib/gitlab/ldap/access_spec.rb
@@ -4,7 +4,7 @@ describe Gitlab::LDAP::Access, lib: true do
let(:access) { Gitlab::LDAP::Access.new user }
let(:user) { create(:omniauth_user) }
- describe :allowed? do
+ describe '#allowed?' do
subject { access.allowed? }
context 'when the user cannot be found' do
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index 03199a2523e..949f6e2b19a 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -25,7 +25,7 @@ describe Gitlab::LDAP::User, lib: true do
OmniAuth::AuthHash.new(uid: 'my-uid', provider: 'ldapmain', info: info_upper_case)
end
- describe :changed? do
+ describe '#changed?' do
it "marks existing ldap user as changed" do
create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain')
expect(ldap_user.changed?).to be_truthy
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 34507cf5083..4e5481f9154 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -15,7 +15,7 @@ describe Ci::Pipeline, models: true do
it { is_expected.to respond_to :git_author_email }
it { is_expected.to respond_to :short_sha }
- describe :valid_commit_sha do
+ describe '#valid_commit_sha' do
context 'commit.sha can not start with 00000000' do
before do
pipeline.sha = '0' * 40
@@ -26,7 +26,7 @@ describe Ci::Pipeline, models: true do
end
end
- describe :short_sha do
+ describe '#short_sha' do
subject { pipeline.short_sha }
it 'has 8 items' do
@@ -35,10 +35,10 @@ describe Ci::Pipeline, models: true do
it { expect(pipeline.sha).to start_with(subject) }
end
- describe :create_next_builds do
+ describe '#create_next_builds' do
end
- describe :retried do
+ describe '#retried' do
subject { pipeline.retried }
before do
@@ -51,7 +51,7 @@ describe Ci::Pipeline, models: true do
end
end
- describe :create_builds do
+ describe '#create_builds' do
let!(:pipeline) { FactoryGirl.create :ci_pipeline, project: project, ref: 'master', tag: false }
def create_builds(trigger_request = nil)
diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb
index 98f60087cf5..4e7833c3162 100644
--- a/spec/models/ci/variable_spec.rb
+++ b/spec/models/ci/variable_spec.rb
@@ -9,7 +9,7 @@ describe Ci::Variable, models: true do
subject.value = secret_value
end
- describe :value do
+ describe '#value' do
it 'stores the encrypted value' do
expect(subject.encrypted_value).not_to be_nil
end
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index 96397d7c8a9..05f22c7a9eb 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -24,14 +24,14 @@ describe CommitStatus, models: true do
it { is_expected.to respond_to :running? }
it { is_expected.to respond_to :pending? }
- describe :author do
+ describe '#author' do
subject { commit_status.author }
before { commit_status.author = User.new }
it { is_expected.to eq(commit_status.user) }
end
- describe :started? do
+ describe '#started?' do
subject { commit_status.started? }
context 'without started_at' do
@@ -57,7 +57,7 @@ describe CommitStatus, models: true do
end
end
- describe :active? do
+ describe '#active?' do
subject { commit_status.active? }
%w(pending running).each do |state|
@@ -77,7 +77,7 @@ describe CommitStatus, models: true do
end
end
- describe :complete? do
+ describe '#complete?' do
subject { commit_status.complete? }
%w(success failed canceled).each do |state|
@@ -97,7 +97,7 @@ describe CommitStatus, models: true do
end
end
- describe :duration do
+ describe '#duration' do
subject { commit_status.duration }
it { is_expected.to eq(120.0) }
@@ -122,7 +122,7 @@ describe CommitStatus, models: true do
end
end
- describe :latest do
+ describe '.latest' do
subject { CommitStatus.latest.order(:id) }
before do
@@ -138,7 +138,7 @@ describe CommitStatus, models: true do
end
end
- describe :running_or_pending do
+ describe '.running_or_pending' do
subject { CommitStatus.running_or_pending.order(:id) }
before do
diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb
index 0344dae8b5d..5e652660e2c 100644
--- a/spec/models/concerns/mentionable_spec.rb
+++ b/spec/models/concerns/mentionable_spec.rb
@@ -7,7 +7,7 @@ describe Mentionable do
nil
end
- describe :references do
+ describe 'references' do
let(:project) { create(:project) }
it 'excludes JIRA references' do
diff --git a/spec/models/forked_project_link_spec.rb b/spec/models/forked_project_link_spec.rb
index fa1a0d4e0c7..f94987dcaff 100644
--- a/spec/models/forked_project_link_spec.rb
+++ b/spec/models/forked_project_link_spec.rb
@@ -18,7 +18,7 @@ describe ForkedProjectLink, "add link on fork" do
end
end
-describe :forked_from_project do
+describe '#forked?' do
let(:forked_project_link) { build(:forked_project_link) }
let(:project_from) { create(:project) }
let(:project_to) { create(:project, forked_project_link: forked_project_link) }
diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb
index c4e781dd1dc..615cfe3142b 100644
--- a/spec/models/generic_commit_status_spec.rb
+++ b/spec/models/generic_commit_status_spec.rb
@@ -4,33 +4,33 @@ describe GenericCommitStatus, models: true do
let(:pipeline) { FactoryGirl.create :ci_pipeline }
let(:generic_commit_status) { FactoryGirl.create :generic_commit_status, pipeline: pipeline }
- describe :context do
+ describe '#context' do
subject { generic_commit_status.context }
before { generic_commit_status.context = 'my_context' }
it { is_expected.to eq(generic_commit_status.name) }
end
- describe :tags do
+ describe '#tags' do
subject { generic_commit_status.tags }
it { is_expected.to eq([:external]) }
end
- describe :set_default_values do
+ describe 'set_default_values' do
before do
generic_commit_status.context = nil
generic_commit_status.stage = nil
generic_commit_status.save
end
- describe :context do
+ describe '#context' do
subject { generic_commit_status.context }
it { is_expected.not_to be_nil }
end
- describe :stage do
+ describe '#stage' do
subject { generic_commit_status.stage }
it { is_expected.not_to be_nil }
diff --git a/spec/models/global_milestone_spec.rb b/spec/models/global_milestone_spec.rb
index 197c99cd007..ae77ec5b348 100644
--- a/spec/models/global_milestone_spec.rb
+++ b/spec/models/global_milestone_spec.rb
@@ -14,7 +14,7 @@ describe GlobalMilestone, models: true do
let(:milestone2_project2) { create(:milestone, title: "VD-123", project: project2) }
let(:milestone2_project3) { create(:milestone, title: "VD-123", project: project3) }
- describe :build_collection do
+ describe '.build_collection' do
before do
milestones =
[
@@ -42,7 +42,7 @@ describe GlobalMilestone, models: true do
end
end
- describe :initialize do
+ describe '#initialize' do
before do
milestones =
[
@@ -63,7 +63,7 @@ describe GlobalMilestone, models: true do
end
end
- describe :safe_title do
+ describe '#safe_title' do
let(:milestone) { create(:milestone, title: "git / test", project: project1) }
it 'should strip out slashes and spaces' do
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index a878ff1b227..266c46213a6 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -97,22 +97,22 @@ describe Group, models: true do
end
end
- describe :users do
+ describe '#users' do
it { expect(group.users).to eq(group.owners) }
end
- describe :human_name do
+ describe '#human_name' do
it { expect(group.human_name).to eq(group.name) }
end
- describe :add_users do
+ describe '#add_user' do
let(:user) { create(:user) }
before { group.add_user(user, GroupMember::MASTER) }
it { expect(group.group_members.masters.map(&:user)).to include(user) }
end
- describe :add_users do
+ describe '#add_users' do
let(:user) { create(:user) }
before { group.add_users([user.id], GroupMember::GUEST) }
@@ -124,7 +124,7 @@ describe Group, models: true do
end
end
- describe :avatar_type do
+ describe '#avatar_type' do
let(:user) { create(:user) }
before { group.add_user(user, GroupMember::MASTER) }
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index 4c103462433..ba622dfb9be 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -101,7 +101,7 @@ describe ProjectMember, models: true do
end
end
- describe :add_users_into_projects do
+ describe '.add_users_into_projects' do
before do
@project_1 = create :project
@project_2 = create :project
@@ -123,7 +123,7 @@ describe ProjectMember, models: true do
it { expect(@project_2.users).to include(@user_2) }
end
- describe :truncate_teams do
+ describe '.truncate_teams' do
before do
@project_1 = create :project
@project_2 = create :project
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index 1e18c788b50..d661dc0e59a 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -70,7 +70,7 @@ describe Milestone, models: true do
end
end
- describe :expired? do
+ describe '#expired?' do
context "expired" do
before do
allow(milestone).to receive(:due_date).and_return(Date.today.prev_year)
@@ -88,7 +88,7 @@ describe Milestone, models: true do
end
end
- describe :percent_complete do
+ describe '#percent_complete' do
before do
allow(milestone).to receive_messages(
closed_items_count: 3,
@@ -111,11 +111,11 @@ describe Milestone, models: true do
it { expect(milestone.is_empty?(user)).to be_falsey }
end
- describe :can_be_closed? do
+ describe '#can_be_closed?' do
it { expect(milestone.can_be_closed?).to be_truthy }
end
- describe :total_items_count do
+ describe '#total_items_count' do
before do
create :closed_issue, milestone: milestone
create :merge_request, milestone: milestone
@@ -126,7 +126,7 @@ describe Milestone, models: true do
end
end
- describe :can_be_closed? do
+ describe '#can_be_closed?' do
before do
milestone = create :milestone
create :closed_issue, milestone: milestone
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 5f68cd2b066..a162da0208e 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -18,11 +18,11 @@ describe Namespace, models: true do
it { is_expected.to respond_to(:to_param) }
end
- describe :to_param do
+ describe '#to_param' do
it { expect(namespace.to_param).to eq(namespace.path) }
end
- describe :human_name do
+ describe '#human_name' do
it { expect(namespace.human_name).to eq(namespace.owner_name) }
end
@@ -54,7 +54,7 @@ describe Namespace, models: true do
end
end
- describe :move_dir do
+ describe '#move_dir' do
before do
@namespace = create :namespace
@project = create :project, namespace: @namespace
@@ -98,7 +98,7 @@ describe Namespace, models: true do
end
end
- describe :find_by_path_or_name do
+ describe '.find_by_path_or_name' do
before do
@namespace = create(:namespace, name: 'WoW', path: 'woW')
end
diff --git a/spec/models/project_security_spec.rb b/spec/models/project_security_spec.rb
index e12258c0874..2142c7c13ef 100644
--- a/spec/models/project_security_spec.rb
+++ b/spec/models/project_security_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Project, models: true do
- describe :authorization do
+ describe 'authorization' do
before do
@p1 = create(:project)
diff --git a/spec/models/project_services/buildkite_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb
index 60364df2015..0866e1532dd 100644
--- a/spec/models/project_services/buildkite_service_spec.rb
+++ b/spec/models/project_services/buildkite_service_spec.rb
@@ -57,7 +57,7 @@ describe BuildkiteService, models: true do
)
end
- describe :webhook_url do
+ describe '#webhook_url' do
it 'returns the webhook url' do
expect(@service.webhook_url).to eq(
'https://webhook.buildkite.com/deliver/secret-sauce-webhook-token'
@@ -65,7 +65,7 @@ describe BuildkiteService, models: true do
end
end
- describe :commit_status_path do
+ describe '#commit_status_path' do
it 'returns the correct status page' do
expect(@service.commit_status_path('2ab7834c')).to eq(
'https://gitlab.buildkite.com/status/secret-sauce-status-token.json?commit=2ab7834c'
@@ -73,7 +73,7 @@ describe BuildkiteService, models: true do
end
end
- describe :build_page do
+ describe '#build_page' do
it 'returns the correct build page' do
expect(@service.build_page('2ab7834c', nil)).to eq(
'https://buildkite.com/account-name/example-project/builds?commit=2ab7834c'
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index d2269854354..e842c58dd82 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -130,17 +130,35 @@ describe Project, models: true do
end
end
- it 'should not allow an invalid URI as import_url' do
+ it 'does not allow an invalid URI as import_url' do
project2 = build(:project, import_url: 'invalid://')
expect(project2).not_to be_valid
end
- it 'should allow a valid URI as import_url' do
+ it 'does allow a valid URI as import_url' do
project2 = build(:project, import_url: 'ssh://test@gitlab.com/project.git')
expect(project2).to be_valid
end
+
+ it 'does not allow to introduce an empty URI' do
+ project2 = build(:project, import_url: '')
+
+ expect(project2).not_to be_valid
+ end
+
+ it 'does not produce import data on an empty URI' do
+ project2 = build(:project, import_url: '')
+
+ expect(project2.import_data).to be_nil
+ end
+
+ it 'does not produce import data on an invalid URI' do
+ project2 = build(:project, import_url: 'test://')
+
+ expect(project2.import_data).to be_nil
+ end
end
describe 'default_scope' do
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 24e49c8def3..b39b958450c 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -16,7 +16,7 @@ describe Repository, models: true do
repository.commit(merge_commit_sha)
end
- describe :branch_names_contains do
+ describe '#branch_names_contains' do
subject { repository.branch_names_contains(sample_commit.id) }
it { is_expected.to include('master') }
@@ -24,7 +24,7 @@ describe Repository, models: true do
it { is_expected.not_to include('fix') }
end
- describe :tag_names_contains do
+ describe '#tag_names_contains' do
subject { repository.tag_names_contains(sample_commit.id) }
it { is_expected.to include('v1.1.0') }
@@ -72,13 +72,13 @@ describe Repository, models: true do
end
end
- describe :last_commit_for_path do
+ describe '#last_commit_for_path' do
subject { repository.last_commit_for_path(sample_commit.id, '.gitignore').id }
it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') }
end
- describe :find_commits_by_message do
+ describe '#find_commits_by_message' do
subject { repository.find_commits_by_message('submodule').map{ |k| k.id } }
it { is_expected.to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
@@ -87,7 +87,7 @@ describe Repository, models: true do
it { is_expected.not_to include('913c66a37b4a45b9769037c55c2d238bd0942d2e') }
end
- describe :blob_at do
+ describe '#blob_at' do
context 'blank sha' do
subject { repository.blob_at(Gitlab::Git::BLANK_SHA, '.gitignore') }
@@ -95,7 +95,7 @@ describe Repository, models: true do
end
end
- describe :merged_to_root_ref? do
+ describe '#merged_to_root_ref?' do
context 'merged branch' do
subject { repository.merged_to_root_ref?('improve/awesome') }
@@ -103,7 +103,7 @@ describe Repository, models: true do
end
end
- describe :can_be_merged? do
+ describe '#can_be_merged?' do
context 'mergeable branches' do
subject { repository.can_be_merged?('0b4bc9a49b562e85de7cc9e834518ea6828729b9', 'master') }
@@ -305,7 +305,7 @@ describe Repository, models: true do
end
end
- describe :add_branch do
+ describe '#add_branch' do
context 'when pre hooks were successful' do
it 'should run without errors' do
hook = double(trigger: [true, nil])
@@ -349,7 +349,7 @@ describe Repository, models: true do
end
end
- describe :rm_branch do
+ describe '#rm_branch' do
context 'when pre hooks were successful' do
it 'should run without errors' do
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil])
@@ -386,7 +386,7 @@ describe Repository, models: true do
end
end
- describe :commit_with_hooks do
+ describe '#commit_with_hooks' do
context 'when pre hooks were successful' do
before do
expect_any_instance_of(GitHooksService).to receive(:execute).
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index 96bbbec9ea1..67b3783d514 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -22,11 +22,11 @@ describe Service, models: true do
@testable = @service.can_test?
end
- describe :can_test do
+ describe '#can_test?' do
it { expect(@testable).to eq(true) }
end
- describe :test do
+ describe '#test' do
let(:data) { 'test' }
it 'test runs execute' do
@@ -45,7 +45,7 @@ describe Service, models: true do
@testable = @service.can_test?
end
- describe :can_test do
+ describe '#can_test?' do
it { expect(@testable).to eq(true) }
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 3984b30ddf8..ff39f187759 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -427,7 +427,7 @@ describe User, models: true do
end
end
- describe :not_in_project do
+ describe '.not_in_project' do
before do
User.delete_all
@user = create :user
@@ -598,7 +598,7 @@ describe User, models: true do
end
end
- describe :avatar_type do
+ describe '#avatar_type' do
let(:user) { create(:user) }
it "should be true if avatar is image" do
@@ -612,7 +612,7 @@ describe User, models: true do
end
end
- describe :requires_ldap_check? do
+ describe '#requires_ldap_check?' do
let(:user) { User.new }
it 'is false when LDAP is disabled' do
@@ -651,7 +651,7 @@ describe User, models: true do
end
context 'ldap synchronized user' do
- describe :ldap_user? do
+ describe '#ldap_user?' do
it 'is true if provider name starts with ldap' do
user = create(:omniauth_user, provider: 'ldapmain')
expect(user.ldap_user?).to be_truthy
@@ -668,7 +668,7 @@ describe User, models: true do
end
end
- describe :ldap_identity do
+ describe '#ldap_identity' do
it 'returns ldap identity' do
user = create :omniauth_user
expect(user.ldap_identity.provider).not_to be_empty
@@ -825,7 +825,7 @@ describe User, models: true do
end
end
- describe :can_be_removed? do
+ describe '#can_be_removed?' do
subject { create(:user) }
context 'no owned groups' do
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 8a52725a893..152cd802839 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -81,6 +81,18 @@ describe API::API, api: true do
expect(json_response.first.keys).not_to include('open_issues_count')
end
+ context 'GET /projects?simple=true' do
+ it 'returns a simplified version of all the projects' do
+ expected_keys = ["id", "http_url_to_repo", "web_url", "name", "name_with_namespace", "path", "path_with_namespace"]
+
+ get api('/projects?simple=true', user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first.keys).to match_array expected_keys
+ end
+ end
+
context 'and using search' do
it 'should return searched project' do
get api('/projects', user), { search: project.name }
diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb
index ae4b7aca820..b72e0bd3dbe 100644
--- a/spec/services/ci/create_trigger_request_service_spec.rb
+++ b/spec/services/ci/create_trigger_request_service_spec.rb
@@ -9,7 +9,7 @@ describe Ci::CreateTriggerRequestService, services: true do
stub_ci_pipeline_to_return_yaml_file
end
- describe :execute do
+ describe '#execute' do
context 'valid params' do
subject { service.execute(project, trigger, 'master') }
diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb
index 476a888e394..3a3e3efe709 100644
--- a/spec/services/ci/image_for_build_service_spec.rb
+++ b/spec/services/ci/image_for_build_service_spec.rb
@@ -8,7 +8,7 @@ module Ci
let(:commit) { project.ensure_pipeline(commit_sha, 'master') }
let(:build) { FactoryGirl.create(:ci_build, pipeline: commit) }
- describe :execute do
+ describe '#execute' do
before { build }
context 'branch name' do
diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb
index f28f2f1438d..026d0ca6534 100644
--- a/spec/services/ci/register_build_service_spec.rb
+++ b/spec/services/ci/register_build_service_spec.rb
@@ -13,7 +13,7 @@ module Ci
specific_runner.assign_to(project)
end
- describe :execute do
+ describe '#execute' do
context 'runner follow tag list' do
it "picks build with the same tag" do
pending_build.tag_list = ["linux"]
diff --git a/spec/services/create_commit_builds_service_spec.rb b/spec/services/create_commit_builds_service_spec.rb
index 309213bd44c..4d09bc5fb12 100644
--- a/spec/services/create_commit_builds_service_spec.rb
+++ b/spec/services/create_commit_builds_service_spec.rb
@@ -9,7 +9,7 @@ describe CreateCommitBuildsService, services: true do
stub_ci_pipeline_to_return_yaml_file
end
- describe :execute do
+ describe '#execute' do
context 'valid params' do
let(:pipeline) do
service.execute(project, user,
diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb
index f6dc9d4008f..789836f71bb 100644
--- a/spec/services/event_create_service_spec.rb
+++ b/spec/services/event_create_service_spec.rb
@@ -4,7 +4,7 @@ describe EventCreateService, services: true do
let(:service) { EventCreateService.new }
describe 'Issues' do
- describe :open_issue do
+ describe '#open_issue' do
let(:issue) { create(:issue) }
it { expect(service.open_issue(issue, issue.author)).to be_truthy }
@@ -14,7 +14,7 @@ describe EventCreateService, services: true do
end
end
- describe :close_issue do
+ describe '#close_issue' do
let(:issue) { create(:issue) }
it { expect(service.close_issue(issue, issue.author)).to be_truthy }
@@ -24,7 +24,7 @@ describe EventCreateService, services: true do
end
end
- describe :reopen_issue do
+ describe '#reopen_issue' do
let(:issue) { create(:issue) }
it { expect(service.reopen_issue(issue, issue.author)).to be_truthy }
@@ -36,7 +36,7 @@ describe EventCreateService, services: true do
end
describe 'Merge Requests' do
- describe :open_mr do
+ describe '#open_mr' do
let(:merge_request) { create(:merge_request) }
it { expect(service.open_mr(merge_request, merge_request.author)).to be_truthy }
@@ -46,7 +46,7 @@ describe EventCreateService, services: true do
end
end
- describe :close_mr do
+ describe '#close_mr' do
let(:merge_request) { create(:merge_request) }
it { expect(service.close_mr(merge_request, merge_request.author)).to be_truthy }
@@ -56,7 +56,7 @@ describe EventCreateService, services: true do
end
end
- describe :merge_mr do
+ describe '#merge_mr' do
let(:merge_request) { create(:merge_request) }
it { expect(service.merge_mr(merge_request, merge_request.author)).to be_truthy }
@@ -66,7 +66,7 @@ describe EventCreateService, services: true do
end
end
- describe :reopen_mr do
+ describe '#reopen_mr' do
let(:merge_request) { create(:merge_request) }
it { expect(service.reopen_mr(merge_request, merge_request.author)).to be_truthy }
@@ -80,7 +80,7 @@ describe EventCreateService, services: true do
describe 'Milestone' do
let(:user) { create :user }
- describe :open_milestone do
+ describe '#open_milestone' do
let(:milestone) { create(:milestone) }
it { expect(service.open_milestone(milestone, user)).to be_truthy }
@@ -90,7 +90,7 @@ describe EventCreateService, services: true do
end
end
- describe :close_mr do
+ describe '#close_mr' do
let(:milestone) { create(:milestone) }
it { expect(service.close_milestone(milestone, user)).to be_truthy }
@@ -100,7 +100,7 @@ describe EventCreateService, services: true do
end
end
- describe :destroy_mr do
+ describe '#destroy_mr' do
let(:milestone) { create(:milestone) }
it { expect(service.destroy_milestone(milestone, user)).to be_truthy }
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index 62b25709a5d..67a919ba8ee 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -12,7 +12,7 @@ describe Issues::CloseService, services: true do
project.team << [user2, :developer]
end
- describe :execute do
+ describe '#execute' do
context "valid params" do
before do
perform_enqueued_jobs do
diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb
index 8443a00e70c..c1db4f3284b 100644
--- a/spec/services/merge_requests/close_service_spec.rb
+++ b/spec/services/merge_requests/close_service_spec.rb
@@ -12,7 +12,7 @@ describe MergeRequests::CloseService, services: true do
project.team << [user2, :developer]
end
- describe :execute do
+ describe '#execute' do
context 'valid params' do
let(:service) { MergeRequests::CloseService.new(project, user, {}) }
diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb
index e433f49872d..d0b55d2d509 100644
--- a/spec/services/merge_requests/create_service_spec.rb
+++ b/spec/services/merge_requests/create_service_spec.rb
@@ -5,7 +5,7 @@ describe MergeRequests::CreateService, services: true do
let(:user) { create(:user) }
let(:assignee) { create(:user) }
- describe :execute do
+ describe '#execute' do
context 'valid params' do
let(:opts) do
{
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index 2f72cd60071..f5bf3c1e367 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -11,7 +11,7 @@ describe MergeRequests::MergeService, services: true do
project.team << [user2, :developer]
end
- describe :execute do
+ describe '#execute' do
context 'valid params' do
let(:service) { MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') }
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 7d5cb876063..06f56d85aa8 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -5,7 +5,7 @@ describe MergeRequests::RefreshService, services: true do
let(:user) { create(:user) }
let(:service) { MergeRequests::RefreshService }
- describe :execute do
+ describe '#execute' do
before do
@user = create(:user)
group = create(:group)
diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb
index ac0221998f5..88c9c640514 100644
--- a/spec/services/merge_requests/reopen_service_spec.rb
+++ b/spec/services/merge_requests/reopen_service_spec.rb
@@ -11,7 +11,7 @@ describe MergeRequests::ReopenService, services: true do
project.team << [user2, :developer]
end
- describe :execute do
+ describe '#execute' do
context 'valid params' do
let(:service) { MergeRequests::ReopenService.new(project, user, {}) }
diff --git a/spec/services/milestones/close_service_spec.rb b/spec/services/milestones/close_service_spec.rb
index 1cd6eb2ab38..5d400299be0 100644
--- a/spec/services/milestones/close_service_spec.rb
+++ b/spec/services/milestones/close_service_spec.rb
@@ -9,7 +9,7 @@ describe Milestones::CloseService, services: true do
project.team << [user, :master]
end
- describe :execute do
+ describe '#execute' do
before do
Milestones::CloseService.new(project, user, {}).execute(milestone)
end
diff --git a/spec/services/milestones/create_service_spec.rb b/spec/services/milestones/create_service_spec.rb
index c793026e300..6d29edb449a 100644
--- a/spec/services/milestones/create_service_spec.rb
+++ b/spec/services/milestones/create_service_spec.rb
@@ -4,7 +4,7 @@ describe Milestones::CreateService, services: true do
let(:project) { create(:empty_project) }
let(:user) { create(:user) }
- describe :execute do
+ describe '#execute' do
context "valid params" do
before do
project.team << [user, :master]
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index 35f576874b8..32753e84b31 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -5,7 +5,7 @@ describe Notes::CreateService, services: true do
let(:issue) { create(:issue, project: project) }
let(:user) { create(:user) }
- describe :execute do
+ describe '#execute' do
context "valid params" do
before do
project.team << [user, :master]
diff --git a/spec/services/notes/post_process_service_spec.rb b/spec/services/notes/post_process_service_spec.rb
index d4c50f824c1..e33a611929b 100644
--- a/spec/services/notes/post_process_service_spec.rb
+++ b/spec/services/notes/post_process_service_spec.rb
@@ -5,7 +5,7 @@ describe Notes::PostProcessService, services: true do
let(:issue) { create(:issue, project: project) }
let(:user) { create(:user) }
- describe :execute do
+ describe '#execute' do
before do
project.team << [user, :master]
note_opts = {
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index d3dddfb4817..9fc93f325f7 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -50,7 +50,7 @@ describe NotificationService, services: true do
update_custom_notification(:new_note, @u_custom_global)
end
- describe :new_note do
+ describe '#new_note' do
it do
add_users_with_subscription(note.project, issue)
@@ -306,7 +306,7 @@ describe NotificationService, services: true do
project.team << [merge_request.assignee, :master]
end
- describe :new_note do
+ describe '#new_note' do
it "records sent notifications" do
# Ensure create SentNotification by noteable = merge_request 6 times, not noteable = note
expect(SentNotification).to receive(:record_note).with(note, any_args).exactly(4).times.and_call_original
diff --git a/spec/services/test_hook_service_spec.rb b/spec/services/test_hook_service_spec.rb
index f034f251ba4..4f47e89b4b5 100644
--- a/spec/services/test_hook_service_spec.rb
+++ b/spec/services/test_hook_service_spec.rb
@@ -5,7 +5,7 @@ describe TestHookService, services: true do
let(:project) { create :project }
let(:hook) { create :project_hook, project: project }
- describe :execute do
+ describe '#execute' do
it "should execute successfully" do
stub_request(:post, hook.url).to_return(status: 200)
expect(TestHookService.new.execute(hook, user)).to be_truthy
diff --git a/spec/support/login_helpers.rb b/spec/support/login_helpers.rb
index ffdf2bb0a8a..e5f76afbfc0 100644
--- a/spec/support/login_helpers.rb
+++ b/spec/support/login_helpers.rb
@@ -37,6 +37,40 @@ module LoginHelpers
Thread.current[:current_user] = user
end
+ def login_via(provider, user, uid)
+ mock_auth_hash(provider, uid, user.email)
+ visit new_user_session_path
+ click_link provider
+ end
+
+ def mock_auth_hash(provider, uid, email)
+ # The mock_auth configuration allows you to set per-provider (or default)
+ # authentication hashes to return during integration testing.
+ OmniAuth.config.mock_auth[provider.to_sym] = OmniAuth::AuthHash.new({
+ provider: provider,
+ uid: uid,
+ info: {
+ name: 'mockuser',
+ email: email,
+ image: 'mock_user_thumbnail_url'
+ },
+ credentials: {
+ token: 'mock_token',
+ secret: 'mock_secret'
+ },
+ extra: {
+ raw_info: {
+ info: {
+ name: 'mockuser',
+ email: email,
+ image: 'mock_user_thumbnail_url'
+ }
+ }
+ }
+ })
+ Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[:saml]
+ end
+
# Requires Javascript driver.
def logout
find(".header-user-dropdown-toggle").click
diff --git a/spec/support/omni_auth.rb b/spec/support/omni_auth.rb
new file mode 100644
index 00000000000..0b1af4052ff
--- /dev/null
+++ b/spec/support/omni_auth.rb
@@ -0,0 +1 @@
+OmniAuth.config.test_mode = true