Commit bfcee4a5 authored by Alfredo Sumaran's avatar Alfredo Sumaran

Merge branch 'master' into issue_14952

parents 946b4519 b6d5fcd4
......@@ -2,7 +2,6 @@ image: "ruby:2.1"
services:
- mysql:latest
- postgres:latest
- redis:latest
cache:
......@@ -35,123 +34,79 @@ spec:feature:
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:feature
tags:
- ruby
- mysql
spec:api:
stage: test
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:api
tags:
- ruby
- mysql
spec:models:
stage: test
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models
tags:
- ruby
- mysql
spec:lib:
stage: test
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib
tags:
- ruby
- mysql
spec:services:
stage: test
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services
tags:
- ruby
- mysql
spec:other:
stage: test
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:other
tags:
- ruby
- mysql
spinach:project:half:
stage: test
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half
tags:
- ruby
- mysql
spinach:project:rest:
stage: test
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest
tags:
- ruby
- mysql
spinach:other:
stage: test
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other
tags:
- ruby
- mysql
teaspoon:
stage: test
script:
- RAILS_ENV=test bundle exec teaspoon
tags:
- ruby
- mysql
rubocop:
stage: test
script:
- bundle exec rubocop
tags:
- ruby
- mysql
scss-lint:
stage: test
script:
- bundle exec rake scss_lint
tags:
- ruby
brakeman:
stage: test
script:
- bundle exec rake brakeman
tags:
- ruby
- mysql
flog:
stage: test
script:
- bundle exec rake flog
tags:
- ruby
- mysql
flay:
stage: test
script:
- bundle exec rake flay
tags:
- ruby
- mysql
bundler:audit:
stage: test
......@@ -159,9 +114,6 @@ bundler:audit:
- master
script:
- "bundle exec bundle-audit check --update --ignore OSVDB-115941"
tags:
- ruby
- mysql
# Ruby 2.2 jobs
......@@ -177,9 +129,6 @@ spec:feature:ruby22:
key: "ruby22"
paths:
- vendor
tags:
- ruby
- mysql
spec:api:ruby22:
stage: test
......@@ -192,9 +141,6 @@ spec:api:ruby22:
key: "ruby22"
paths:
- vendor
tags:
- ruby
- mysql
spec:models:ruby22:
stage: test
......@@ -207,9 +153,6 @@ spec:models:ruby22:
key: "ruby22"
paths:
- vendor
tags:
- ruby
- mysql
spec:lib:ruby22:
stage: test
......@@ -222,9 +165,6 @@ spec:lib:ruby22:
key: "ruby22"
paths:
- vendor
tags:
- ruby
- mysql
spec:services:ruby22:
stage: test
......@@ -237,9 +177,6 @@ spec:services:ruby22:
key: "ruby22"
paths:
- vendor
tags:
- ruby
- mysql
spec:other:ruby22:
stage: test
......@@ -252,9 +189,6 @@ spec:other:ruby22:
key: "ruby22"
paths:
- vendor
tags:
- ruby
- mysql
spinach:project:half:ruby22:
stage: test
......@@ -268,9 +202,6 @@ spinach:project:half:ruby22:
key: "ruby22"
paths:
- vendor
tags:
- ruby
- mysql
spinach:project:rest:ruby22:
stage: test
......@@ -284,9 +215,6 @@ spinach:project:rest:ruby22:
key: "ruby22"
paths:
- vendor
tags:
- ruby
- mysql
spinach:other:ruby22:
stage: test
......@@ -300,10 +228,6 @@ spinach:other:ruby22:
key: "ruby22"
paths:
- vendor
tags:
- ruby
- mysql
notify:slack:
stage: notifications
......
......@@ -14,6 +14,7 @@ v 8.7.0 (unreleased)
- Add links to CI setup documentation from project settings and builds pages
- Handle nil descriptions in Slack issue messages (Stan Hu)
- Add default scope to projects to exclude projects pending deletion
- Ensure empty recipients are rejected in BuildsEmailService
- Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.)
- Implement 'TODOs View' as an option for dashboard preferences !3379 (Elias W.)
- Gracefully handle notes on deleted commits in merge requests (Stan Hu)
......@@ -22,6 +23,7 @@ v 8.7.0 (unreleased)
- Remove "Congratulations!" tweet button on newly-created project. (Connor Shea)
- Improved UX of the navigation sidebar
- Build status notifications
- API: Expose user location (Robert Schilling)
v 8.6.5 (unreleased)
- Check permissions when user attempts to import members from another project
......
......@@ -177,10 +177,11 @@ class GitLabDropdown
selector = ".dropdown-page-one .dropdown-content a"
@dropdown.on "click", selector, (e) ->
selected = self.rowClicked $(@)
$el = $(@)
selected = self.rowClicked $el
if self.options.clicked
self.options.clicked(selected)
self.options.clicked(selected, $el, e)
# Finds an element inside wrapper element
getElement: (selector) ->
......@@ -360,6 +361,8 @@ class GitLabDropdown
# Toggle the dropdown label
if @options.toggleLabel
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel
else
selectedObject
else
if !value?
field.remove()
......@@ -375,7 +378,7 @@ class GitLabDropdown
if @options.toggleLabel
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selectedObject)
if value?
if !field.length
if !field.length and fieldName
# Create hidden input for form
input = "<input type='hidden' name='#{fieldName}' value='#{value}' />"
if @options.inputId?
......@@ -394,7 +397,7 @@ class GitLabDropdown
selector = ".dropdown-page-one #{selector}"
# simulate a click on the first link
$(selector).trigger "click"
$(selector, @dropdown).trigger "click"
addArrowKeyEvent: ->
ARROW_KEY_CODES = [38, 40]
......
......@@ -26,6 +26,20 @@
$(".selected_issue").bind "change", Issues.checkChanged
# Update state filters if present in page
updateStateFilters: ->
stateFilters = $('.issues-state-filters')
newParams = {}
paramKeys = ['author_id', 'label_name', 'milestone_title', 'assignee_id', 'issue_search']
for paramKey in paramKeys
newParams[paramKey] = gl.utils.getUrlParameter(paramKey) or ''
if stateFilters.length
stateFilters.find('a').each ->
initialUrl = $(this).attr 'href'
$(this).attr 'href', gl.utils.mergeUrlParams(newParams, initialUrl)
# Make sure we trigger ajax request only after user stop typing
initSearch: ->
@timer = null
......@@ -54,6 +68,7 @@
# Change url so if user reload a page - search results are saved
history.replaceState {page: issuesUrl}, document.title, issuesUrl
Issues.reload()
Issues.updateStateFilters()
dataType: "json"
checkChanged: ->
......
((w) ->
w.gl ?= {}
w.gl.utils ?= {}
w.gl.utils.getUrlParameter = (sParam) ->
sPageURL = decodeURIComponent(window.location.search.substring(1))
sURLVariables = sPageURL.split('&')
sParameterName = undefined
i = 0
while i < sURLVariables.length
sParameterName = sURLVariables[i].split('=')
if sParameterName[0] is sParam
return if sParameterName[1] is undefined then true else sParameterName[1]
i++
# #
# @param {Object} params - url keys and value to merge
# @param {String} url
# #
w.gl.utils.mergeUrlParams = (params, url) ->
newUrl = decodeURIComponent(url)
for paramName, paramValue of params
pattern = new RegExp "\\b(#{paramName}=).*?(&|$)"
if url.search(pattern) >= 0
newUrl = newUrl.replace pattern, "$1#{paramValue}$2"
else
newUrl = "#{newUrl}#{(if newUrl.indexOf('?') > 0 then '&' else '?')}#{paramName}=#{paramValue}"
newUrl
) window
......@@ -62,6 +62,8 @@ class @SearchAutocomplete
search:
fields: ['text']
data: @getData.bind(@)
selectable: true
clicked: @onClick.bind(@)
getData: (term, callback) ->
_this = @
......@@ -102,6 +104,8 @@ class @SearchAutocomplete
lastCategory = suggestion.category
data.push
id: "#{suggestion.category.toLowerCase()}-#{suggestion.id}"
category: suggestion.category
text: suggestion.label
url: suggestion.url
......@@ -133,12 +137,19 @@ class @SearchAutocomplete
}
bindEvents: ->
$(document).on 'click', @onDocumentClick
@searchInput.on 'keydown', @onSearchInputKeyDown
@searchInput.on 'keyup', @onSearchInputKeyUp
@searchInput.on 'click', @onSearchInputClick
@searchInput.on 'focus', @onSearchInputFocus
@searchInput.on 'blur', @onSearchInputBlur
@clearInput.on 'click', @onRemoveLocationClick
@clearInput.on 'click', @onClearInputClick
onDocumentClick: (e) =>
# If clicking outside the search box
# And search input is not focused
# And we are not clicking inside a suggestion
if not $.contains(@dropdown[0], e.target) and @isFocused and not $(e.target).parents('ul').length
@onSearchInputBlur()
enableAutocomplete: ->
# No need to enable anything if user is not logged in
......@@ -181,6 +192,8 @@ class @SearchAutocomplete
# We should display the menu only when input is not empty
@enableAutocomplete()
@wrap.toggleClass 'has-value', !!e.target.value
# Avoid falsy value to be returned
return
......@@ -189,27 +202,20 @@ class @SearchAutocomplete
e.stopImmediatePropagation()
onSearchInputFocus: =>
@isFocused = true
@wrap.addClass('search-active')
onRemoveLocationClick: (e) =>
onClearInputClick: (e) =>
e.preventDefault()
@removeLocationBadge()
@searchInput.val('').focus()
@skipBlurEvent = true
onSearchInputBlur: (e) =>
@skipBlurEvent = false
# We should wait to make sure we are not clearing the input instead
setTimeout( =>
return if @skipBlurEvent
@isFocused = false
@wrap.removeClass('search-active')
@wrap.removeClass('search-active')
# If input is blank then restore state
if @searchInput.val() is ''
@restoreOriginalState()
, 150)
# If input is blank then restore state
if @searchInput.val() is ''
@restoreOriginalState()
addLocationBadge: (item) ->
category = if item.category? then "#{item.category}: " else ''
......@@ -268,3 +274,23 @@ class @SearchAutocomplete
<li><a class='dropdown-menu-empty-link is-focused'>Loading...</a></li>
</ul>"
@dropdownContent.html(html)
onClick: (item, $el, e) ->
if location.pathname.indexOf(item.url) isnt -1
e.preventDefault()
if not @badgePresent
if item.category is 'Projects'
@projectInputEl.val(item.id)
@addLocationBadge(
value: 'This project'
)
if item.category is 'Groups'
@groupInputEl.val(item.id)
@addLocationBadge(
value: 'This group'
)
$el.removeClass('is-active')
@disableAutocomplete()
@searchInput.val('').focus()
......@@ -104,9 +104,9 @@ $orange-light: rgba(252, 109, 38, 0.80);
$orange-normal: #e75e40;
$orange-dark: #ce5237;
$red-light: #f06559;
$red-normal: #e52c5a;
$red-dark: #d22852;
$red-light: #e52c5a;
$red-normal: #d22852;
$red-dark: darken($red-normal, 5%);
$border-white-light: #f1f2f4;
$border-white-normal: #d6dae2;
......@@ -128,9 +128,9 @@ $border-orange-light: #fc6d26;
$border-orange-normal: #ce5237;
$border-orange-dark: #c14e35;
$border-red-light: #f24f41;
$border-red-normal: #d22852;
$border-red-dark: #ca264f;
$border-red-light: #d22852;
$border-red-normal: #ca264f;
$border-red-dark: darken($border-red-normal, 5%);
$help-well-bg: #fafafa;
$help-well-border: #e5e5e5;
......@@ -201,14 +201,14 @@ $award-emoji-new-btn-icon-color: #dcdcdc;
/*
* Search Box
*/
$search-input-border-color: $dropdown-input-focus-border;
$search-input-border-color: rgba(#4688f1, .8);
$search-input-focus-shadow-color: $dropdown-input-focus-shadow;
$search-input-width: $dropdown-width;
$search-input-width: 244px;
$location-badge-color: #aaa;
$location-badge-bg: $gray-normal;
$location-badge-active-bg: #4f91f8;
$location-icon-color: #e7e9ed;
$location-active-color: $gl-text-color;
$location-active-bg: $search-input-border-color;
$location-icon-active-color: #807e7e;
/*
* Notes
......
......@@ -315,7 +315,7 @@ pre.light-well {
}
.git-empty {
margin: 0 7px;
margin: 0 7px 7px;
h5 {
color: #5c5d5e;
......@@ -401,7 +401,7 @@ pre.light-well {
}
.commit_short_id {
margin-right: 5px;
margin: 0 5px;
color: $gl-link-color;
font-weight: 600;
}
......
......@@ -135,25 +135,25 @@
.location-badge {
@include transition(all .15s);
background-color: $location-active-bg;
background-color: $location-badge-active-bg;
color: $white-light;
}
.search-input-wrap {
i {
color: $location-active-color;
color: $location-icon-active-color;
}
}
}
&.has-location-badge {
.search-icon {
display: none;
}
&.has-value {
.search-icon {
display: none;
}
.clear-icon {
cursor: pointer;
display: block;
}
.clear-icon {
cursor: pointer;
display: block;
}
}
......
.container-fluid .content {
.container-fluid {
.ci-status {
padding: 2px 7px;
margin-right: 5px;
......
......@@ -50,12 +50,15 @@ class BuildsEmailService < Service
def execute(push_data)
return unless supported_events.include?(push_data[:object_kind])
return unless should_build_be_notified?(push_data)
if should_build_be_notified?(push_data)
recipients = all_recipients(push_data)
if recipients.any?
BuildEmailWorker.perform_async(
push_data[:build_id],
all_recipients(push_data),
push_data,
recipients,
push_data
)
end
end
......@@ -84,7 +87,7 @@ class BuildsEmailService < Service
end
def all_recipients(data)
all_recipients = recipients.split(',')
all_recipients = recipients.split(',').compact.reject(&:blank?)
if add_pusher? && data[:user][:email]
all_recipients << "#{data[:user][:email]}"
......
......@@ -21,8 +21,8 @@
%a.is-focused.dropdown-menu-empty-link
Loading...
= dropdown_loading
%i.search-icon
%i.clear-icon.js-clear-input
%i.search-icon
%i.clear-icon.js-clear-input
= hidden_field_tag :group_id, @group.try(:id)
= hidden_field_tag :project_id, @project && @project.persisted? ? @project.id : '', id: 'search_project_id'
......
......@@ -18,7 +18,7 @@
= access
= link_to '#', title: 'Edit comment', class: 'note-action-button js-note-edit' do
= icon('pencil-square-o')
= link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button js-note-delete danger' do
= link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button js-note-delete' do
= icon('trash-o')
.note-body{class: note_editable?(note) ? 'js-task-list-container' : ''}
.note-text
......
......@@ -69,6 +69,7 @@ GET /users
"state": "blocked",
"created_at": "2012-05-23T08:01:01Z",
"bio": null,
"location": null,
"skype": "",
"linkedin": "",
"twitter": "",
......@@ -126,6 +127,7 @@ Parameters:
"created_at": "2012-05-23T08:00:58Z",
"is_admin": false,
"bio": null,
"location": null,
"skype": "",
"linkedin": "",
"twitter": "",
......@@ -154,6 +156,7 @@ Parameters:
"confirmed_at": "2012-05-23T08:00:58Z",
"last_sign_in_at": "2015-03-23T08:00:58Z",
"bio": null,
"location": null,
"skype": "",
"linkedin": "",
"twitter": "",
......@@ -191,6 +194,7 @@ Parameters:
- `extern_uid` (optional) - External UID
- `provider` (optional) - External provider name
- `bio` (optional) - User's biography
- `location` (optional) - User's location
- `admin` (optional) - User is admin - true or false (default)
- `can_create_group` (optional) - User can create groups - true or false
- `confirm` (optional) - Require confirmation - true (default) or false
......@@ -218,6 +222,7 @@ Parameters:
- `extern_uid` - External UID
- `provider` - External provider name
- `bio` - User's biography
- `location` (optional) - User's location
- `admin` (optional) - User is admin - true or false (default)
- `can_create_group` (optional) - User can create groups - true or false
- `external` (optional) - Flags the user as external - true or false(default)
......@@ -260,6 +265,7 @@ GET /user
"state": "active",
"created_at": "2012-05-23T08:00:58Z",
"bio": null,
"location": null,
"skype": "",
"linkedin": "",
"twitter": "",
......
......@@ -4,6 +4,7 @@
- [CI setup](ci_setup.md) for testing GitLab
- [Gotchas](gotchas.md) to avoid
- [How to dump production data to staging](db_dump.md)
- [Instrumentation](instrumentation.md)
- [Migration Style Guide](migration_style_guide.md) for creating safe migrations
- [Rake tasks](rake_tasks.md) for development
- [Shell commands](shell_commands.md) in the GitLab codebase
......
# Instrumenting Ruby Code
GitLab Performance Monitoring allows instrumenting of custom blocks of Ruby
code. This can be used to measure the time spent in a specific part of a larger
chunk of code. The resulting data is written to a separate series.
To start measuring a block of Ruby code you should use
`Gitlab::Metrics.measure` and give it a name for the series to store the data
in:
```ruby
Gitlab::Metrics.measure(:user_logins) do
...
end
```
The first argument of this method is the series name and should be plural. This
name will be prefixed with `rails_` or `sidekiq_` depending on whether the code
was run in the Rails application or one of the Sidekiq workers. In the
above example the final series names would be as follows:
- rails_user_logins
- sidekiq_user_logins
Series names should be plural as this keeps the naming style in line with the
other series names.
By default metrics measured using a block contain a single value, "duration",
which contains the number of milliseconds it took to execute the block. Custom
values can be added by passing a Hash as the 2nd argument. Custom tags can be
added by passing a Hash as the 3rd argument. A simple example is as follows:
```ruby
Gitlab::Metrics.measure(:example_series, { number: 10 }, { class: self.class.to_s }) do
...
end
```
......@@ -15,7 +15,7 @@ module API
class User < UserBasic
expose :created_at
expose :is_admin?, as: :is_admin
expose :bio, :skype, :linkedin, :twitter, :website_url
expose :bio, :location, :skype, :linkedin, :twitter, :website_url
end
class Identity < Grape::Entity
......
......@@ -58,6 +58,7 @@ module API
# extern_uid - External authentication provider UID
# provider - External provider
# bio - Bio
# location - Location of the user
# admin - User is admin - true or false (default)
# can_create_group - User can create groups - true or false
# confirm - Require user confirmation - true (default) or false
......@@ -67,7 +68,7 @@ module API
post do
authenticated_as_admin!
required_attributes! [:email, :password, :name, :username]
attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :bio, :can_create_group, :admin, :confirm, :external]
attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :bio, :location, :can_create_group, :admin, :confirm, :external]
admin = attrs.delete(:admin)
confirm = !(attrs.delete(:confirm) =~ (/(false|f|no|0)$/i))
user = User.build_user(attrs)
......@@ -106,6 +107,7 @@ module API
# website_url - Website url
# projects_limit - Limit projects each user can create
# bio - Bio
# location - Location of the user
# admin - User is admin - true or false (default)
# can_create_group - User can create groups - true or false
# external - Flags the user as external - true or false(default)
......@@ -114,7 +116,7 @@ module API
put ":id" do
authenticated_as_admin!
attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :website_url, :projects_limit, :username, :bio, :can_create_group, :admin, :external]
attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :website_url, :projects_limit, :username, :bio, :location, :can_create_group, :admin, :external]
user = User.find(params[:id])
not_found!('User') unless user
......
......@@ -119,7 +119,7 @@ module Banzai
elsif element_node?(node)
yield_valid_link(node) do |link, text|
if ref_pattern && link =~ /\A#{ref_pattern}/
if ref_pattern && link =~ /\A#{ref_pattern}\z/
replace_link_node_with_href(node, link) do
object_link_filter(link, ref_pattern, link_text: text)
end
......
......@@ -70,6 +70,32 @@ module Gitlab
value.to_s.gsub('=', '\\=')
end
# Measures the execution time of a block.
#
# Example:
#
# Gitlab::Metrics.measure(:find_by_username_timings) do
# User.find_by_username(some_username)
# end
#
# series - The name of the series to store the data in.
# values - A Hash containing extra values to add to the metric.
# tags - A Hash containing extra tags to add to the metric.
#
# Returns the value yielded by the supplied block.
def self.measure(series, values = {}, tags = {})
return yield unless Transaction.current
start = Time.now.to_f
retval = yield
duration = (Time.now.to_f - start) * 1000.0
values = values.merge(duration: duration)
Transaction.current.add_metric(series, values, tags)
retval
end
# When enabled this should be set before being used as the usual pattern
# "@foo ||= bar" is _not_ thread-safe.
if enabled?
......
require 'rails_helper'
describe 'Filter issues', feature: true do
let!(:project) { create(:project) }
let!(:user) { create(:user)}
let!(:milestone) { create(:milestone, project: project) }
let!(:label) { create(:label, project: project) }
before do
project.team << [user, :master]
login_as(user)
end
describe 'Filter issues for assignee from issues#index' do
before do
visit namespace_project_issues_path(project.namespace, project)
find('.js-assignee-search').click
find('.dropdown-menu-user-link', text: user.username).click
sleep 2
end
context 'assignee', js: true do
it 'should update to current user' do
expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name)
end
it 'should not change when closed link is clicked' do
find('.issues-state-filters a', text: "Closed").click
expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name)
end
it 'should not change when all link is clicked' do
find('.issues-state-filters a', text: "All").click
expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name)
end
end
end
describe 'Filter issues for milestone from issues#index' do
before do
visit namespace_project_issues_path(project.namespace, project)
find('.js-milestone-select').click
find('.milestone-filter .dropdown-content a', text: milestone.title).click
sleep 2
end
context 'milestone', js: true do
it 'should update to current milestone' do
expect(find('.js-milestone-select .dropdown-toggle-text')).to have_content(milestone.title)
end
it 'should not change when closed link is clicked' do
find('.issues-state-filters a', text: "Closed").click
expect(find('.js-milestone-select .dropdown-toggle-text')).to have_content(milestone.title)
end
it 'should not change when all link is clicked' do
find('.issues-state-filters a', text: "All").click
expect(find('.js-milestone-select .dropdown-toggle-text')).to have_content(milestone.title)
end
end
end
describe 'Filter issues for assignee and label from issues#index' do
before do
visit namespace_project_issues_path(project.namespace, project)
find('.js-assignee-search').click
find('.dropdown-menu-user-link', text: user.username).click
sleep 2
find('.js-label-select').click
find('.dropdown-menu-labels .dropdown-content a', text: label.title).click
sleep 2
end
context 'assignee and label', js: true do
it 'should update to current assignee and label' do
expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name)
expect(find('.js-label-select .dropdown-toggle-text')).to have_content(label.title)
end
it 'should not change when closed link is clicked' do
find('.issues-state-filters a', text: "Closed").click
expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name)
expect(find('.js-label-select .dropdown-toggle-text')).to have_content(label.title)
end
it 'should not change when all link is clicked' do
find('.issues-state-filters a', text: "All").click
expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name)
expect(find('.js-label-select .dropdown-toggle-text')).to have_content(label.title)
end
end
end
end
......@@ -95,6 +95,14 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
result = reference_pipeline_result("Fixed #{reference}")
expect(result[:references][:issue]).to eq [issue]
end
it 'does not process links containing issue numbers followed by text' do
href = "#{reference}st"
doc = reference_filter("<a href='#{href}'></a>")
link = doc.css('a').first.attr('href')
expect(link).to eq(href)
end
end
context 'cross-project reference' do
......
......@@ -13,7 +13,7 @@ describe Gitlab::Metrics do
end
end
describe '#submit_metrics' do
describe '.submit_metrics' do
it 'prepares and writes the metrics to InfluxDB' do
connection = double(:connection)
pool = double(:pool)
......@@ -26,7 +26,7 @@ describe Gitlab::Metrics do
end
end
describe '#prepare_metrics' do
describe '.prepare_metrics' do
it 'returns a Hash with the keys as Symbols' do
metrics = described_class.
prepare_metrics([{ 'values' => {}, 'tags' => {} }])
......@@ -51,7 +51,7 @@ describe Gitlab::Metrics do
end
end
describe '#escape_value' do
describe '.escape_value' do
it 'escapes an equals sign' do
expect(described_class.escape_value('foo=')).to eq('foo\\=')
end
......@@ -60,4 +60,45 @@ describe Gitlab::Metrics do
expect(described_class.escape_value(10)).to eq('10')
end
end
describe '.measure' do
context 'without a transaction' do
it 'returns the return value of the block' do
val = Gitlab::Metrics.measure(:foo) { 10 }
expect(val).to eq(10)
end
end
context 'with a transaction' do
let(:transaction) { Gitlab::Metrics::Transaction.new }
before do
allow(Gitlab::Metrics::Transaction).to receive(:current).
and_return(transaction)
end
it 'adds a metric to the current transaction' do
expect(transaction).to receive(:add_metric).
with(:foo, { duration: a_kind_of(Numeric) }, { tag: 'value' })
Gitlab::Metrics.measure(:foo, {}, tag: 'value') { 10 }
end
it 'supports adding of custom values' do
values = { duration: a_kind_of(Numeric), number: 10 }
expect(transaction).to receive(:add_metric).
with(:foo, values, { tag: 'value' })
Gitlab::Metrics.measure(:foo, { number: 10 }, tag: 'value') { 10 }
end
it 'returns the return value of the block' do
val = Gitlab::Metrics.measure(:foo) { 10 }
expect(val).to eq(10)
end
end
end
end
......@@ -6,18 +6,38 @@ describe BuildsEmailService do
let(:service) { BuildsEmailService.new }
describe :execute do
it "sends email" do
it 'sends email' do
service.recipients = 'test@gitlab.com'
data[:build_status] = 'failed'
expect(BuildEmailWorker).to receive(:perform_async)
service.execute(data)
end
it "does not sends email with failed build and allowed_failure on" do
it 'does not send email with succeeded build and notify_only_broken_builds on' do
expect(service).to receive(:notify_only_broken_builds).and_return(true)
data[:build_status] = 'success'
expect(BuildEmailWorker).not_to receive(:perform_async)
service.execute(data)
end
it 'does not send email with failed build and build_allow_failure is true' do
data[:build_status] = 'failed'
data[:build_allow_failure] = true
expect(BuildEmailWorker).not_to receive(:perform_async)
service.execute(data)
end
it 'does not send email with unknown build status' do
data[:build_status] = 'foo'
expect(BuildEmailWorker).not_to receive(:perform_async)
service.execute(data)
end
it 'does not send email when recipients list is empty' do
service.recipients = ' ,, '
data[:build_status] = 'failed'
expect(BuildEmailWorker).not_to receive(:perform_async)
service.execute(data)
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment