Commit a49151f0 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'upstream/master' into wall-clock-time-for-showing-pipeline

* upstream/master: (50 commits)
  Increased vertical alignment of labels for issues in lists
  Changed file name Updated spec HAML
  Changed tests to use JS tests
  Addressed feedback
  Made logic simpler by moving away from underscorejs
  Added tooltip to label value in collapsed sidebar
  Add play icon SVG
  Have hover color of builds span full width
  Fix alignment of icon on commits page
  Change sleep to wait_for_ajax
  Added tests
  Destroy branch delete tooltip when row is removed
  Move and improvement comment in pipeline fixtures
  Fix notification_service argument error of declined invitation emails
  Update contribution acceptance criteria with tests requirements
  Fixed keyboard shortcuts not working on issue boards
  Hides tooltip when dragging Fixes issue with cursor not changing when dragging
  Hides tooltips when dragging issues
  Add a spec testing a second side effect of `Repository#merge`.
  drop execute bit
  ...
parents f8496a33 1f127006
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 8.11.0 (unreleased) v 8.11.0 (unreleased)
- Use test coverage value from the latest successful pipeline in badge. !5862
- Add test coverage report badge. !5708 - Add test coverage report badge. !5708
- Remove the http_parser.rb dependency by removing the tinder gem. !5758 (tbalthazar) - Remove the http_parser.rb dependency by removing the tinder gem. !5758 (tbalthazar)
- Ability to specify branches for Pivotal Tracker integration (Egor Lynko) - Ability to specify branches for Pivotal Tracker integration (Egor Lynko)
...@@ -18,13 +19,16 @@ v 8.11.0 (unreleased) ...@@ -18,13 +19,16 @@ v 8.11.0 (unreleased)
- API: Endpoints for enabling and disabling deploy keys - API: Endpoints for enabling and disabling deploy keys
- API: List access requests, request access, approve, and deny access requests to a project or a group. !4833 - API: List access requests, request access, approve, and deny access requests to a project or a group. !4833
- Use long options for curl examples in documentation !5703 (winniehell) - Use long options for curl examples in documentation !5703 (winniehell)
- Added tooltip listing label names to the labels value in the collapsed issuable sidebar
- Remove magic comments (`# encoding: UTF-8`) from Ruby files. !5456 (winniehell) - Remove magic comments (`# encoding: UTF-8`) from Ruby files. !5456 (winniehell)
- Fix badge count alignment (ClemMakesApps)
- GitLab Performance Monitoring can now track custom events such as the number of tags pushed to a repository - GitLab Performance Monitoring can now track custom events such as the number of tags pushed to a repository
- Add support for relative links starting with ./ or / to RelativeLinkFilter (winniehell) - Add support for relative links starting with ./ or / to RelativeLinkFilter (winniehell)
- Allow naming U2F devices !5833 - Allow naming U2F devices !5833
- Ignore URLs starting with // in Markdown links !5677 (winniehell) - Ignore URLs starting with // in Markdown links !5677 (winniehell)
- Fix CI status icon link underline (ClemMakesApps) - Fix CI status icon link underline (ClemMakesApps)
- The Repository class is now instrumented - The Repository class is now instrumented
- Fix commit mention font inconsistency (ClemMakesApps)
- Fix filter label tooltip HTML rendering (ClemMakesApps) - Fix filter label tooltip HTML rendering (ClemMakesApps)
- Cache the commit author in RequestStore to avoid extra lookups in PostReceive - Cache the commit author in RequestStore to avoid extra lookups in PostReceive
- Expand commit message width in repo view (ClemMakesApps) - Expand commit message width in repo view (ClemMakesApps)
...@@ -56,6 +60,7 @@ v 8.11.0 (unreleased) ...@@ -56,6 +60,7 @@ v 8.11.0 (unreleased)
- Enforce 2FA restrictions on API authentication endpoints !5820 - Enforce 2FA restrictions on API authentication endpoints !5820
- Limit git rev-list output count to one in forced push check - Limit git rev-list output count to one in forced push check
- Show deployment status on merge requests with external URLs - Show deployment status on merge requests with external URLs
- Fix branch title trailing space on hover (ClemMakesApps)
- Clean up unused routes (Josef Strzibny) - Clean up unused routes (Josef Strzibny)
- Fix issue on empty project to allow developers to only push to protected branches if given permission - Fix issue on empty project to allow developers to only push to protected branches if given permission
- API: Add enpoints for pipelines - API: Add enpoints for pipelines
...@@ -72,6 +77,7 @@ v 8.11.0 (unreleased) ...@@ -72,6 +77,7 @@ v 8.11.0 (unreleased)
- Fix devise deprecation warnings. - Fix devise deprecation warnings.
- Check for 2FA when using Git over HTTP and only allow PersonalAccessTokens as password in that case !5764 - Check for 2FA when using Git over HTTP and only allow PersonalAccessTokens as password in that case !5764
- Update version_sorter and use new interface for faster tag sorting - Update version_sorter and use new interface for faster tag sorting
- Load branches asynchronously in Cherry Pick and Revert dialogs.
- Optimize checking if a user has read access to a list of issues !5370 - Optimize checking if a user has read access to a list of issues !5370
- Store all DB secrets in secrets.yml, under descriptive names !5274 - Store all DB secrets in secrets.yml, under descriptive names !5274
- Fix syntax highlighting in file editor - Fix syntax highlighting in file editor
...@@ -106,12 +112,14 @@ v 8.11.0 (unreleased) ...@@ -106,12 +112,14 @@ v 8.11.0 (unreleased)
- Fix search for notes which belongs to deleted objects - Fix search for notes which belongs to deleted objects
- Allow Akismet to be trained by submitting issues as spam or ham !5538 - Allow Akismet to be trained by submitting issues as spam or ham !5538
- Add GitLab Workhorse version to admin dashboard (Katarzyna Kobierska Ula Budziszewska) - Add GitLab Workhorse version to admin dashboard (Katarzyna Kobierska Ula Budziszewska)
- Fix spacing and vertical alignment on build status icon on commits page (ClemMakesApps)
- Allow branch names ending with .json for graph and network page !5579 (winniehell) - Allow branch names ending with .json for graph and network page !5579 (winniehell)
- Add the `sprockets-es6` gem - Add the `sprockets-es6` gem
- Improve OAuth2 client documentation (muteor) - Improve OAuth2 client documentation (muteor)
- Fix diff comments inverted toggle bug (ClemMakesApps) - Fix diff comments inverted toggle bug (ClemMakesApps)
- Multiple trigger variables show in separate lines (Katarzyna Kobierska Ula Budziszewska) - Multiple trigger variables show in separate lines (Katarzyna Kobierska Ula Budziszewska)
- Profile requests when a header is passed - Profile requests when a header is passed
- Fix button missing type (ClemMakesApps)
- Avoid calculation of line_code and position for _line partial when showing diff notes on discussion tab. - Avoid calculation of line_code and position for _line partial when showing diff notes on discussion tab.
- Speedup DiffNote#active? on discussions, preloading noteables and avoid touching git repository to return diff_refs when possible - Speedup DiffNote#active? on discussions, preloading noteables and avoid touching git repository to return diff_refs when possible
- Add commit stats in commit api. !5517 (dixpac) - Add commit stats in commit api. !5517 (dixpac)
...@@ -120,6 +128,7 @@ v 8.11.0 (unreleased) ...@@ -120,6 +128,7 @@ v 8.11.0 (unreleased)
- edit_blob_link will use blob passed onto the options parameter - edit_blob_link will use blob passed onto the options parameter
- Make error pages responsive (Takuya Noguchi) - Make error pages responsive (Takuya Noguchi)
- The performance of the project dropdown used for moving issues has been improved - The performance of the project dropdown used for moving issues has been improved
- Move to project dropdown with infinite scroll for better performance
- Fix skip_repo parameter being ignored when destroying a namespace - Fix skip_repo parameter being ignored when destroying a namespace
- Add all builds into stage/job dropdowns on builds page - Add all builds into stage/job dropdowns on builds page
- Change requests_profiles resource constraint to catch virtually any file - Change requests_profiles resource constraint to catch virtually any file
...@@ -128,6 +137,7 @@ v 8.11.0 (unreleased) ...@@ -128,6 +137,7 @@ v 8.11.0 (unreleased)
- Sensible state specific default sort order for issues and merge requests !5453 (tomb0y) - Sensible state specific default sort order for issues and merge requests !5453 (tomb0y)
- Fix bug where destroying a namespace would not always destroy projects - Fix bug where destroying a namespace would not always destroy projects
- Fix RequestProfiler::Middleware error when code is reloaded in development - Fix RequestProfiler::Middleware error when code is reloaded in development
- Allow horizontal scrolling of code blocks in issue body
- Catch what warden might throw when profiling requests to re-throw it - Catch what warden might throw when profiling requests to re-throw it
- Avoid commit lookup on diff_helper passing existing local variable to the helper method - Avoid commit lookup on diff_helper passing existing local variable to the helper method
- Add description to new_issue email and new_merge_request_email in text/plain content type. !5663 (dixpac) - Add description to new_issue email and new_merge_request_email in text/plain content type. !5663 (dixpac)
...@@ -142,6 +152,7 @@ v 8.11.0 (unreleased) ...@@ -142,6 +152,7 @@ v 8.11.0 (unreleased)
- Each `File::exists?` replaced to `File::exist?` because of deprecate since ruby version 2.2.0 - Each `File::exists?` replaced to `File::exist?` because of deprecate since ruby version 2.2.0
- Add auto-completition in pipeline (Katarzyna Kobierska Ula Budziszewska) - Add auto-completition in pipeline (Katarzyna Kobierska Ula Budziszewska)
- Add pipelines tab to merge requests - Add pipelines tab to merge requests
- Fix notification_service argument error of declined invitation emails
- Fix a memory leak caused by Banzai::Filter::SanitizationFilter - Fix a memory leak caused by Banzai::Filter::SanitizationFilter
- Speed up todos queries by limiting the projects set we join with - Speed up todos queries by limiting the projects set we join with
- Ensure file editing in UI does not overwrite commited changes without warning user - Ensure file editing in UI does not overwrite commited changes without warning user
......
...@@ -387,7 +387,8 @@ description area. Copy-paste it to retain the markdown format. ...@@ -387,7 +387,8 @@ description area. Copy-paste it to retain the markdown format.
1. The change is as small as possible 1. The change is as small as possible
1. Include proper tests and make all tests pass (unless it contains a test 1. Include proper tests and make all tests pass (unless it contains a test
exposing a bug in existing code) exposing a bug in existing code). Every new class should have corresponding
unit tests, even if the class is exercised at a higher level, such as a feature test.
1. If you suspect a failing CI build is unrelated to your contribution, you may 1. If you suspect a failing CI build is unrelated to your contribution, you may
try and restart the failing CI job or ask a developer to fix the try and restart the failing CI job or ask a developer to fix the
aforementioned failing test aforementioned failing test
......
...@@ -153,7 +153,9 @@ ...@@ -153,7 +153,9 @@
}); });
}); });
$('.remove-row').bind('ajax:success', function() { $('.remove-row').bind('ajax:success', function() {
return $(this).closest('li').fadeOut(); $(this).tooltip('destroy')
.closest('li')
.fadeOut();
}); });
$('.js-remove-tr').bind('ajax:before', function() { $('.js-remove-tr').bind('ajax:before', function() {
return $(this).hide(); return $(this).hide();
......
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
draggable: '.is-draggable', draggable: '.is-draggable',
handle: '.js-board-handle', handle: '.js-board-handle',
onEnd: (e) => { onEnd: (e) => {
document.body.classList.remove('is-dragging'); gl.issueBoards.onEnd();
if (e.newIndex !== undefined && e.oldIndex !== e.newIndex) { if (e.newIndex !== undefined && e.oldIndex !== e.newIndex) {
const order = this.sortable.toArray(), const order = this.sortable.toArray(),
......
...@@ -63,6 +63,8 @@ ...@@ -63,6 +63,8 @@
Store.moving.issue = card.issue; Store.moving.issue = card.issue;
Store.moving.list = card.list; Store.moving.list = card.list;
gl.issueBoards.onStart();
}, },
onAdd: (e) => { onAdd: (e) => {
gl.issueBoards.BoardsStore.moveIssueToList(Store.moving.list, this.list, Store.moving.issue); gl.issueBoards.BoardsStore.moveIssueToList(Store.moving.list, this.list, Store.moving.issue);
......
...@@ -2,6 +2,17 @@ ...@@ -2,6 +2,17 @@
window.gl = window.gl || {}; window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {}; window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.onStart = () => {
$('.has-tooltip').tooltip('hide')
.tooltip('disable');
document.body.classList.add('is-dragging');
};
gl.issueBoards.onEnd = () => {
$('.has-tooltip').tooltip('enable');
document.body.classList.remove('is-dragging');
};
gl.issueBoards.getBoardSortableDefaultOptions = (obj) => { gl.issueBoards.getBoardSortableDefaultOptions = (obj) => {
let defaultSortOptions = { let defaultSortOptions = {
forceFallback: true, forceFallback: true,
...@@ -11,12 +22,8 @@ ...@@ -11,12 +22,8 @@
filter: '.has-tooltip', filter: '.has-tooltip',
scrollSensitivity: 100, scrollSensitivity: 100,
scrollSpeed: 20, scrollSpeed: 20,
onStart () { onStart: gl.issueBoards.onStart,
document.body.classList.add('is-dragging'); onEnd: gl.issueBoards.onEnd
},
onEnd () {
document.body.classList.remove('is-dragging');
}
} }
Object.keys(obj).forEach((key) => { defaultSortOptions[key] = obj[key]; }); Object.keys(obj).forEach((key) => { defaultSortOptions[key] = obj[key]; });
......
File mode changed from 100755 to 100644
...@@ -20,6 +20,9 @@ ...@@ -20,6 +20,9 @@
path = page.split(':'); path = page.split(':');
shortcut_handler = null; shortcut_handler = null;
switch (page) { switch (page) {
case 'projects:boards:show':
shortcut_handler = new ShortcutsNavigation();
break;
case 'projects:issues:index': case 'projects:issues:index':
Issuable.init(); Issuable.init();
new IssuableBulkActions(); new IssuableBulkActions();
......
...@@ -102,20 +102,34 @@ ...@@ -102,20 +102,34 @@
}; };
IssuableForm.prototype.initMoveDropdown = function() { IssuableForm.prototype.initMoveDropdown = function() {
var $moveDropdown; var $moveDropdown, pageSize;
$moveDropdown = $('.js-move-dropdown'); $moveDropdown = $('.js-move-dropdown');
if ($moveDropdown.length) { if ($moveDropdown.length) {
pageSize = $moveDropdown.data('page-size');
return $('.js-move-dropdown').select2({ return $('.js-move-dropdown').select2({
ajax: { ajax: {
url: $moveDropdown.data('projects-url'), url: $moveDropdown.data('projects-url'),
results: function(data) { quietMillis: 125,
data: function(term, page, context) {
return { return {
results: data search: term,
offset_id: context
}; };
}, },
data: function(query) { results: function(data) {
var context,
more;
if (data.length >= pageSize)
more = true;
if (data[data.length - 1])
context = data[data.length - 1].id;
return { return {
search: query results: data,
more: more,
context: context
}; };
} }
}, },
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
var _this; var _this;
_this = this; _this = this;
$('.js-label-select').each(function(i, dropdown) { $('.js-label-select').each(function(i, dropdown) {
var $block, $colorPreview, $dropdown, $form, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, defaultLabel, enableLabelCreateButton, issueURLSplit, issueUpdateURL, labelHTMLTemplate, labelNoneHTMLTemplate, labelUrl, projectId, saveLabelData, selectedLabel, showAny, showNo; var $block, $colorPreview, $dropdown, $form, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, defaultLabel, enableLabelCreateButton, issueURLSplit, issueUpdateURL, labelHTMLTemplate, labelNoneHTMLTemplate, labelUrl, projectId, saveLabelData, selectedLabel, showAny, showNo, $sidebarLabelTooltip;
$dropdown = $(dropdown); $dropdown = $(dropdown);
projectId = $dropdown.data('project-id'); projectId = $dropdown.data('project-id');
labelUrl = $dropdown.data('labels'); labelUrl = $dropdown.data('labels');
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
$block = $selectbox.closest('.block'); $block = $selectbox.closest('.block');
$form = $dropdown.closest('form'); $form = $dropdown.closest('form');
$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span'); $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span');
$sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip');
$value = $block.find('.value'); $value = $block.find('.value');
$loading = $block.find('.block-loading').fadeOut(); $loading = $block.find('.block-loading').fadeOut();
if (issueUpdateURL != null) { if (issueUpdateURL != null) {
...@@ -31,7 +32,11 @@ ...@@ -31,7 +32,11 @@
labelNoneHTMLTemplate = '<span class="no-value">None</span>'; labelNoneHTMLTemplate = '<span class="no-value">None</span>';
} }
new gl.CreateLabelDropdown($dropdown.closest('.dropdown').find('.dropdown-new-label'), projectId); $sidebarLabelTooltip.tooltip();
if ($dropdown.closest('.dropdown').find('.dropdown-new-label').length) {
new gl.CreateLabelDropdown($dropdown.closest('.dropdown').find('.dropdown-new-label'), projectId);
}
saveLabelData = function() { saveLabelData = function() {
var data, selected; var data, selected;
...@@ -52,7 +57,7 @@ ...@@ -52,7 +57,7 @@
dataType: 'JSON', dataType: 'JSON',
data: data data: data
}).done(function(data) { }).done(function(data) {
var labelCount, template; var labelCount, template, labelTooltipTitle, labelTitles;
$loading.fadeOut(); $loading.fadeOut();
$dropdown.trigger('loaded.gl.dropdown'); $dropdown.trigger('loaded.gl.dropdown');
$selectbox.hide(); $selectbox.hide();
...@@ -66,6 +71,27 @@ ...@@ -66,6 +71,27 @@
} }
$value.removeAttr('style').html(template); $value.removeAttr('style').html(template);
$sidebarCollapsedValue.text(labelCount); $sidebarCollapsedValue.text(labelCount);
if (data.labels.length) {
labelTitles = data.labels.map(function(label) {
return label.title;
});
if (labelTitles.length > 5) {
labelTitles = labelTitles.slice(0, 5);
labelTitles.push('and ' + (data.labels.length - 5) + ' more');
}
labelTooltipTitle = labelTitles.join(', ');
} else {
labelTooltipTitle = '';
$sidebarLabelTooltip.tooltip('destroy');
}
$sidebarLabelTooltip
.attr('title', labelTooltipTitle)
.tooltip('fixTitle');
$('.has-tooltip', $value).tooltip({ $('.has-tooltip', $value).tooltip({
container: 'body' container: 'body'
}); });
......
...@@ -65,7 +65,8 @@ ...@@ -65,7 +65,8 @@
url: $dropdown.data('refs-url'), url: $dropdown.data('refs-url'),
data: { data: {
ref: $dropdown.data('ref') ref: $dropdown.data('ref')
} },
dataType: "json"
}).done(function(refs) { }).done(function(refs) {
return callback(refs); return callback(refs);
}); });
...@@ -73,7 +74,7 @@ ...@@ -73,7 +74,7 @@
selectable: true, selectable: true,
filterable: true, filterable: true,
filterByText: true, filterByText: true,
fieldName: 'ref', fieldName: $dropdown.data('field-name'),
renderRow: function(ref) { renderRow: function(ref) {
var link; var link;
if (ref.header != null) { if (ref.header != null) {
......
...@@ -84,6 +84,15 @@ ...@@ -84,6 +84,15 @@
width: 100%; width: 100%;
} }
} }
// Allows dynamic-width text in the dropdown toggle.
// Resizes to allow long text without overflowing the container.
&.dynamic {
width: auto;
min-width: 160px;
max-width: 100%;
padding-right: 25px;
}
} }
.dropdown-menu, .dropdown-menu,
......
...@@ -63,9 +63,10 @@ ...@@ -63,9 +63,10 @@
&.image_file { &.image_file {
background: #eee; background: #eee;
text-align: center; text-align: center;
img { img {
padding: 100px; padding: 20px;
max-width: 50%; max-width: 80%;
} }
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Styles that apply to all GFM related forms. * Styles that apply to all GFM related forms.
*/ */
.gfm-commit, .gfm-commit_range { .gfm-commit_range {
font-family: $monospace_font; font-family: $monospace_font;
font-size: 90%; font-size: 90%;
} }
.modal-body { .modal-body {
position: relative; position: relative;
overflow-y: auto;
padding: 15px; padding: 15px;
.form-actions { .form-actions {
......
...@@ -72,6 +72,7 @@ ...@@ -72,6 +72,7 @@
font-weight: normal; font-weight: normal;
background-color: #eee; background-color: #eee;
color: #78a; color: #78a;
vertical-align: baseline;
} }
} }
......
...@@ -45,7 +45,8 @@ ...@@ -45,7 +45,8 @@
min-width: 175px; min-width: 175px;
} }
.select2-results .select2-result-label { .select2-results .select2-result-label,
.select2-more-results {
padding: 10px 15px; padding: 10px 15px;
} }
......
...@@ -14,12 +14,20 @@ ...@@ -14,12 +14,20 @@
margin-top: 0; margin-top: 0;
} }
// Single code lines should wrap
code { code {
font-family: $monospace_font; font-family: $monospace_font;
white-space: pre; white-space: pre-wrap;
word-wrap: normal; word-wrap: normal;
} }
// Multi-line code blocks should scroll horizontally
pre {
code {
white-space: pre;
}
}
kbd { kbd {
display: inline-block; display: inline-block;
padding: 3px 5px; padding: 3px 5px;
......
...@@ -8,9 +8,13 @@ ...@@ -8,9 +8,13 @@
} }
.is-dragging { .is-dragging {
// Important because plugin sets inline CSS
opacity: 1!important;
* { * {
cursor: -webkit-grabbing; // !important to make sure no style can override this when dragging
cursor: grabbing; cursor: -webkit-grabbing!important;
cursor: grabbing!important;
} }
} }
...@@ -254,11 +258,6 @@ ...@@ -254,11 +258,6 @@
opacity: 0.3; opacity: 0.3;
} }
.is-dragging {
// Important because plugin sets inline CSS
opacity: 1!important;
}
.card { .card {
position: relative; position: relative;
width: 100%; width: 100%;
...@@ -316,6 +315,7 @@ ...@@ -316,6 +315,7 @@
.card-footer { .card-footer {
margin-top: 5px; margin-top: 5px;
line-height: 25px;
.label { .label {
margin-right: 4px; margin-right: 4px;
......
...@@ -168,7 +168,6 @@ ...@@ -168,7 +168,6 @@
text-overflow: ellipsis; text-overflow: ellipsis;
&:hover { &:hover {
background-color: $row-hover;
color: $gl-text-color; color: $gl-text-color;
} }
} }
...@@ -190,6 +189,10 @@ ...@@ -190,6 +189,10 @@
display: block; display: block;
} }
} }
&:hover {
background-color: $row-hover;
}
} }
} }
} }
......
...@@ -66,6 +66,15 @@ ...@@ -66,6 +66,15 @@
margin-left: 8px; margin-left: 8px;
} }
} }
.ci-status-link {
svg {
position: relative;
top: 2px;
margin: 0 2px 0 3px;
}
}
} }
.ci-status-link { .ci-status-link {
......
...@@ -34,11 +34,4 @@ ...@@ -34,11 +34,4 @@
} }
} }
} }
.wiki {
code {
white-space: pre-wrap;
word-break: keep-all;
}
}
} }
...@@ -374,3 +374,10 @@ ...@@ -374,3 +374,10 @@
} }
} }
} }
.merge-request-details {
.title {
margin-bottom: 20px;
}
}
...@@ -300,6 +300,17 @@ ...@@ -300,6 +300,17 @@
&.playable { &.playable {
background-color: $gray-light; background-color: $gray-light;
svg {
height: 12px;
width: 12px;
position: relative;
top: 1px;
path {
fill: $layout-link-gray;
}
}
} }
.build-content { .build-content {
...@@ -319,10 +330,6 @@ ...@@ -319,10 +330,6 @@
margin-right: 5px; margin-right: 5px;
} }
.fa {
font-size: 13px;
}
// Connect first build in each stage with right horizontal line // Connect first build in each stage with right horizontal line
&:first-child { &:first-child {
&::after { &::after {
......
...@@ -15,6 +15,13 @@ class Projects::BranchesController < Projects::ApplicationController ...@@ -15,6 +15,13 @@ class Projects::BranchesController < Projects::ApplicationController
diverging_commit_counts = repository.diverging_commit_counts(branch) diverging_commit_counts = repository.diverging_commit_counts(branch)
[memo, diverging_commit_counts[:behind], diverging_commit_counts[:ahead]].max [memo, diverging_commit_counts[:behind], diverging_commit_counts[:ahead]].max
end end
respond_to do |format|
format.html
format.json do
render json: @repository.branch_names
end
end
end end
def recent def recent
......
class MoveToProjectFinder class MoveToProjectFinder
PAGE_SIZE = 50
def initialize(user) def initialize(user)
@user = user @user = user
end end
...@@ -8,6 +10,10 @@ class MoveToProjectFinder ...@@ -8,6 +10,10 @@ class MoveToProjectFinder
projects = projects.search(search) if search.present? projects = projects.search(search) if search.present?
projects = projects.excluding_project(from_project) projects = projects.excluding_project(from_project)
# infinite scroll using offset
projects = projects.where('projects.id < ?', offset_id) if offset_id.present?
projects = projects.limit(PAGE_SIZE)
# to ask for Project#name_with_namespace # to ask for Project#name_with_namespace
projects.includes(namespace: :owner) projects.includes(namespace: :owner)
end end
......
...@@ -39,7 +39,7 @@ module CiStatusHelper ...@@ -39,7 +39,7 @@ module CiStatusHelper
when 'running' when 'running'
'icon_status_running' 'icon_status_running'
when 'play' when 'play'
return icon('play fw') 'icon_play'
when 'created' when 'created'
'icon_status_pending' 'icon_status_pending'
else else
......
...@@ -72,6 +72,15 @@ module IssuablesHelper ...@@ -72,6 +72,15 @@ module IssuablesHelper
end end
end end
def issuable_labels_tooltip(labels, limit: 5)
first, last = labels.partition.with_index{ |_, i| i < limit }
label_names = first.collect(&:name)
label_names << "and #{last.size} more" unless last.empty?
label_names.join(', ')
end
private private
def sidebar_gutter_collapsed? def sidebar_gutter_collapsed?
......
...@@ -242,7 +242,6 @@ class NotificationService ...@@ -242,7 +242,6 @@ class NotificationService
project_member.real_source_type, project_member.real_source_type,
project_member.project.id, project_member.project.id,
project_member.invite_email, project_member.invite_email,
project_member.access_level,
project_member.created_by_id project_member.created_by_id
).deliver_later ).deliver_later
end end
...@@ -269,7 +268,6 @@ class NotificationService ...@@ -269,7 +268,6 @@ class NotificationService
group_member.real_source_type, group_member.real_source_type,
group_member.group.id, group_member.group.id,
group_member.invite_email, group_member.invite_email,
group_member.access_level,
group_member.created_by_id group_member.created_by_id
).deliver_later ).deliver_later
end end
......
...@@ -65,7 +65,7 @@ ...@@ -65,7 +65,7 @@
Graphs Graphs
- if project_nav_tab? :issues - if project_nav_tab? :issues
= nav_link(controller: [:issues, :labels, :milestones]) do = nav_link(controller: [:issues, :labels, :milestones, :boards]) do
= link_to namespace_project_issues_path(@project.namespace, @project), title: 'Issues', class: 'shortcuts-issues' do = link_to namespace_project_issues_path(@project.namespace, @project), title: 'Issues', class: 'shortcuts-issues' do
%span %span
Issues Issues
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
- number_commits_ahead = diverging_commit_counts[:ahead] - number_commits_ahead = diverging_commit_counts[:ahead]
%li(class="js-branch-#{branch.name}") %li(class="js-branch-#{branch.name}")
%div %div
= link_to namespace_project_tree_path(@project.namespace, @project, branch.name) do = link_to namespace_project_tree_path(@project.namespace, @project, branch.name), class: 'item-title str-truncated' do
%span.item-title.str-truncated= branch.name = branch.name
&nbsp; &nbsp;
- if branch.name == @repository.root_ref - if branch.name == @repository.root_ref
%span.label.label-primary default %span.label.label-primary default
......
...@@ -17,7 +17,9 @@ ...@@ -17,7 +17,9 @@
.form-group.branch .form-group.branch
= label_tag 'target_branch', target_label, class: 'control-label' = label_tag 'target_branch', target_label, class: 'control-label'
.col-sm-10 .col-sm-10
= select_tag "target_branch", project_branches, class: "select2 select2-sm js-target-branch" = hidden_field_tag :target_branch, @project.default_branch, id: 'target_branch'
= dropdown_tag(@project.default_branch, options: { title: "Switch branch", filter: true, placeholder: "Search branches", toggle_class: 'js-project-refs-dropdown js-target-branch dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "target_branch", selected: @project.default_branch, target_branch: @project.default_branch, refs_url: namespace_project_branches_path(@project.namespace, @project), submit_form_on_click: false }})
- if can?(current_user, :push_code, @project) - if can?(current_user, :push_code, @project)
.js-create-merge-request-container .js-create-merge-request-container
.checkbox .checkbox
......
...@@ -56,7 +56,8 @@ ...@@ -56,7 +56,8 @@
= pluralize(@commit.pipelines.count, 'pipeline') = pluralize(@commit.pipelines.count, 'pipeline')
= link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id), class: "ci-status-link ci-status-icon-#{@commit.status}" do = link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id), class: "ci-status-link ci-status-icon-#{@commit.status}" do
= ci_icon_for_status(@commit.status) = ci_icon_for_status(@commit.status)
= ci_label_for_status(@commit.status) %span.ci-status-label
= ci_label_for_status(@commit.status)
in in
= time_interval_in_words @commit.pipelines.total_duration = time_interval_in_words @commit.pipelines.total_duration
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
- if can?(current_user, :create_issue, @project) || can?(current_user, :update_issue, @issue) - if can?(current_user, :create_issue, @project) || can?(current_user, :update_issue, @issue)
.issuable-actions .issuable-actions
.clearfix.issue-btn-group.dropdown .clearfix.issue-btn-group.dropdown
%button.btn.btn-default.pull-left.hidden-md.hidden-lg{ data: { toggle: "dropdown" } } %button.btn.btn-default.pull-left.hidden-md.hidden-lg{ type: "button", data: { toggle: "dropdown" } }
%span.caret %span.caret
Options Options
.dropdown-menu.dropdown-menu-align-right.hidden-lg .dropdown-menu.dropdown-menu-align-right.hidden-lg
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
- if can?(current_user, :update_merge_request, @merge_request) - if can?(current_user, :update_merge_request, @merge_request)
.issuable-actions .issuable-actions
.clearfix.issue-btn-group.dropdown .clearfix.issue-btn-group.dropdown
%button.btn.btn-default.pull-left.hidden-md.hidden-lg{ data: { toggle: "dropdown" } } %button.btn.btn-default.pull-left.hidden-md.hidden-lg{ type: "button", data: { toggle: "dropdown" } }
%span.caret %span.caret
Options Options
.dropdown-menu.dropdown-menu-align-right.hidden-lg .dropdown-menu.dropdown-menu-align-right.hidden-lg
......
- @no_container = true
- page_title "Edit", @tag.name, "Tags" - page_title "Edit", @tag.name, "Tags"
= render "projects/commits/head" = render "projects/commits/head"
.row-content-block %div{ class: container_class }
.oneline .sub-header-block.no-bottom-space
.title .oneline
Release notes for tag .title
%strong #{@tag.name} Release notes for tag
%strong #{@tag.name}
.prepend-top-default
= form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal common-note-form release-form js-quick-submit' }) do |f| = form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal common-note-form release-form js-quick-submit' }) do |f|
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
= render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: "Write your release notes or drag files here..." = render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: "Write your release notes or drag files here..."
= render 'projects/notes/hints' = render 'projects/notes/hints'
.error-alert .error-alert
.form-actions.prepend-top-default .prepend-top-default
= f.submit 'Save changes', class: 'btn btn-save' = f.submit 'Save changes', class: 'btn btn-save'
= link_to "Cancel", namespace_project_tag_path(@project.namespace, @project, @tag.name), class: "btn btn-default btn-cancel" = link_to "Cancel", namespace_project_tag_path(@project.namespace, @project, @tag.name), class: "btn btn-default btn-cancel"
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
- @options && @options.each do |key, value| - @options && @options.each do |key, value|
= hidden_field_tag key, value, id: nil = hidden_field_tag key, value, id: nil
.dropdown .dropdown
= dropdown_toggle dropdown_toggle_text, { toggle: "dropdown", selected: dropdown_toggle_text, ref: @ref, refs_url: refs_namespace_project_path(@project.namespace, @project) }, { toggle_class: "js-project-refs-dropdown" } = dropdown_toggle dropdown_toggle_text, { toggle: "dropdown", selected: dropdown_toggle_text, ref: @ref, refs_url: refs_namespace_project_path(@project.namespace, @project), field_name: 'ref', submit_form_on_click: true }, { toggle_class: "js-project-refs-dropdown" }
.dropdown-menu.dropdown-menu-selectable{ class: ("dropdown-menu-align-right" if local_assigns[:align_right]) } .dropdown-menu.dropdown-menu-selectable{ class: ("dropdown-menu-align-right" if local_assigns[:align_right]) }
= dropdown_title "Switch branch/tag" = dropdown_title "Switch branch/tag"
= dropdown_filter "Search branches and tags" = dropdown_filter "Search branches and tags"
......
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 11"><path fill-rule="evenodd" d="m9.283 6.47l-7.564 4.254c-.949.534-1.719.266-1.719-.576v-9.292c0-.852.756-1.117 1.719-.576l7.564 4.254c.949.534.963 1.392 0 1.934"/></svg>
\ No newline at end of file
...@@ -121,7 +121,7 @@ ...@@ -121,7 +121,7 @@
= label_tag :move_to_project_id, 'Move', class: 'control-label' = label_tag :move_to_project_id, 'Move', class: 'control-label'
.col-sm-10 .col-sm-10
.issuable-form-select-holder .issuable-form-select-holder
= hidden_field_tag :move_to_project_id, nil, class: 'js-move-dropdown', data: { placeholder: 'Select project', projects_url: autocomplete_projects_path(project_id: @project.id) } = hidden_field_tag :move_to_project_id, nil, class: 'js-move-dropdown', data: { placeholder: 'Select project', projects_url: autocomplete_projects_path(project_id: @project.id), page_size: MoveToProjectFinder::PAGE_SIZE }
&nbsp; &nbsp;
%span{ data: { toggle: 'tooltip', placement: 'auto top' }, style: 'cursor: default', %span{ data: { toggle: 'tooltip', placement: 'auto top' }, style: 'cursor: default',
title: 'Moving an issue will copy the discussion to a different project and close it here. All participants will be notified of the new location.' } title: 'Moving an issue will copy the discussion to a different project and close it here. All participants will be notified of the new location.' }
......
...@@ -109,7 +109,7 @@ ...@@ -109,7 +109,7 @@
- if issuable.project.labels.any? - if issuable.project.labels.any?
.block.labels .block.labels
.sidebar-collapsed-icon .sidebar-collapsed-icon.js-sidebar-labels-tooltip{ title: issuable_labels_tooltip(issuable.labels_array), data: { placement: "left", container: "body" } }
= icon('tags') = icon('tags')
%span %span
= issuable.labels_array.size = issuable.labels_array.size
......
class Gitlab::Seeder::Builds class Gitlab::Seeder::Pipelines
STAGES = %w[build test deploy notify] STAGES = %w[build test deploy notify]
BUILDS = [ BUILDS = [
{ name: 'build:linux', stage: 'build', status: :success }, { name: 'build:linux', stage: 'build', status: :success },
...@@ -7,11 +7,12 @@ class Gitlab::Seeder::Builds ...@@ -7,11 +7,12 @@ class Gitlab::Seeder::Builds
{ name: 'rspec:windows', stage: 'test', status: :success }, { name: 'rspec:windows', stage: 'test', status: :success },
{ name: 'rspec:windows', stage: 'test', status: :success }, { name: 'rspec:windows', stage: 'test', status: :success },
{ name: 'rspec:osx', stage: 'test', status_event: :success }, { name: 'rspec:osx', stage: 'test', status_event: :success },
{ name: 'spinach:linux', stage: 'test', status: :pending }, { name: 'spinach:linux', stage: 'test', status: :success },
{ name: 'spinach:osx', stage: 'test', status: :canceled }, { name: 'spinach:osx', stage: 'test', status: :failed, allow_failure: true},
{ name: 'cucumber:linux', stage: 'test', status: :running }, { name: 'env:alpha', stage: 'deploy', environment: 'alpha', status: :pending },
{ name: 'cucumber:osx', stage: 'test', status: :failed }, { name: 'env:beta', stage: 'deploy', environment: 'beta', status: :running },
{ name: 'staging', stage: 'deploy', environment: 'staging', status: :success }, { name: 'env:gamma', stage: 'deploy', environment: 'gamma', status: :canceled },
{ name: 'staging', stage: 'deploy', environment: 'staging', status_event: :success },
{ name: 'production', stage: 'deploy', environment: 'production', when: 'manual', status: :skipped }, { name: 'production', stage: 'deploy', environment: 'production', when: 'manual', status: :skipped },
{ name: 'slack', stage: 'notify', when: 'manual', status: :created }, { name: 'slack', stage: 'notify', when: 'manual', status: :created },
] ]
...@@ -34,72 +35,86 @@ class Gitlab::Seeder::Builds ...@@ -34,72 +35,86 @@ class Gitlab::Seeder::Builds
end end
end end
private
def pipelines def pipelines
master_pipelines + merge_request_pipelines create_master_pipelines + create_merge_request_pipelines
end end
def master_pipelines def create_master_pipelines
create_pipelines_for(@project, 'master') @project.repository.commits('master', limit: 4).map do |commit|
create_pipeline!(@project, 'master', commit)
end
rescue rescue
[] []
end end
def merge_request_pipelines def create_merge_request_pipelines
@project.merge_requests.last(5).map do |merge_request| pipelines = @project.merge_requests.first(3).map do |merge_request|
create_pipelines(merge_request.source_project, merge_request.source_branch, merge_request.commits.last(5)) project = merge_request.source_project
end.flatten branch = merge_request.source_branch
merge_request.commits.last(4).map do |commit|
create_pipeline!(project, branch, commit)
end
end
pipelines.flatten
rescue rescue
[] []
end end
def create_pipelines_for(project, ref)
commits = project.repository.commits(ref, limit: 5) def create_pipeline!(project, ref, commit)
create_pipelines(project, ref, commits) project.pipelines.create(sha: commit.id, ref: ref)
end end
def create_pipelines(project, ref, commits) def build_create!(pipeline, opts = {})
commits.map do |commit| attributes = job_attributes(pipeline, opts)
project.pipelines.create(sha: commit.id, ref: ref) .merge(commands: '$ build command')
Ci::Build.create!(attributes).tap do |build|
# We need to set build trace and artifacts after saving a build
# (id required), that is why we need `#tap` method instead of passing
# block directly to `Ci::Build#create!`.
setup_artifacts(build)
setup_build_log(build)
build.save
end end
end end
def build_create!(pipeline, opts = {}) def setup_artifacts(build)
attributes = build_attributes_for(pipeline, opts) return unless %w[build test].include?(build.stage)
Ci::Build.create!(attributes) do |build| artifacts_cache_file(artifacts_archive_path) do |file|
if opts[:name].start_with?('build') build.artifacts_file = file
artifacts_cache_file(artifacts_archive_path) do |file| end
build.artifacts_file = file
end
artifacts_cache_file(artifacts_metadata_path) do |file| artifacts_cache_file(artifacts_metadata_path) do |file|
build.artifacts_metadata = file build.artifacts_metadata = file
end end
end end
if %w(running success failed).include?(build.status) def setup_build_log(build)
# We need to set build trace after saving a build (id required) if %w(running success failed).include?(build.status)
build.trace = FFaker::Lorem.paragraphs(6).join("\n\n") build.trace = FFaker::Lorem.paragraphs(6).join("\n\n")
end
end end
end end
def commit_status_create!(pipeline, opts = {}) def commit_status_create!(pipeline, opts = {})
attributes = commit_status_attributes_for(pipeline, opts) attributes = job_attributes(pipeline, opts)
GenericCommitStatus.create!(attributes) GenericCommitStatus.create!(attributes)
end end
def commit_status_attributes_for(pipeline, opts) def job_attributes(pipeline, opts)
{ name: 'test build', stage: 'test', stage_idx: stage_index(opts[:stage]), { name: 'test build', stage: 'test', stage_idx: stage_index(opts[:stage]),
ref: 'master', tag: false, user: build_user, project: @project, pipeline: pipeline, ref: 'master', tag: false, user: build_user, project: @project, pipeline: pipeline,
created_at: Time.now, updated_at: Time.now created_at: Time.now, updated_at: Time.now
}.merge(opts) }.merge(opts)
end end
def build_attributes_for(pipeline, opts)
commit_status_attributes_for(pipeline, opts).merge(commands: '$ build command')
end
def build_user def build_user
@project.team.users.sample @project.team.users.sample
end end
...@@ -131,8 +146,8 @@ class Gitlab::Seeder::Builds ...@@ -131,8 +146,8 @@ class Gitlab::Seeder::Builds
end end
Gitlab::Seeder.quiet do Gitlab::Seeder.quiet do
Project.all.sample(10).each do |project| Project.all.sample(5).each do |project|
project_builds = Gitlab::Seeder::Builds.new(project) project_builds = Gitlab::Seeder::Pipelines.new(project)
project_builds.seed! project_builds.seed!
end end
end end
...@@ -67,6 +67,8 @@ use following Markdown code to embed the est coverage report into `README.md`: ...@@ -67,6 +67,8 @@ use following Markdown code to embed the est coverage report into `README.md`:
![coverage](http://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage) ![coverage](http://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)
``` ```
The latest successful pipeline will be used to read the test coverage value.
[builds]: #builds [builds]: #builds
[jobs]: yaml/README.md#jobs [jobs]: yaml/README.md#jobs
[stages]: yaml/README.md#stages [stages]: yaml/README.md#stages
......
...@@ -13,8 +13,7 @@ module Gitlab ...@@ -13,8 +13,7 @@ module Gitlab
@job = job @job = job
@pipeline = @project.pipelines @pipeline = @project.pipelines
.where(ref: @ref) .latest_successful_for(@ref)
.where(sha: @project.commit(@ref).try(:sha))
.first .first
end end
......
...@@ -237,6 +237,56 @@ describe AutocompleteController do ...@@ -237,6 +237,56 @@ describe AutocompleteController do
end end
end end
context 'authorized projects apply limit' do
before do
authorized_project2 = create(:project)
authorized_project3 = create(:project)
authorized_project.team << [user, :master]
authorized_project2.team << [user, :master]
authorized_project3.team << [user, :master]
stub_const 'MoveToProjectFinder::PAGE_SIZE', 2
end
describe 'GET #projects with project ID' do
before do
get(:projects, project_id: project.id)
end
let(:body) { JSON.parse(response.body) }
it do
expect(body).to be_kind_of(Array)
expect(body.size).to eq 3 # Of a total of 4
end
end
end
context 'authorized projects with offset' do
before do
authorized_project2 = create(:project)
authorized_project3 = create(:project)
authorized_project.team << [user, :master]
authorized_project2.team << [user, :master]
authorized_project3.team << [user, :master]
end
describe 'GET #projects with project ID and offset_id' do
before do
get(:projects, project_id: project.id, offset_id: authorized_project.id)
end
let(:body) { JSON.parse(response.body) }
it do
expect(body.detect { |item| item['id'] == 0 }).to be_nil # 'No project' is not there
expect(body.detect { |item| item['id'] == authorized_project.id }).to be_nil # Offset project is not there either
end
end
end
context 'authorized projects without admin_issue ability' do context 'authorized projects without admin_issue ability' do
before(:each) do before(:each) do
authorized_project.team << [user, :guest] authorized_project.team << [user, :guest]
......
...@@ -572,6 +572,18 @@ describe 'Issue Boards', feature: true, js: true do ...@@ -572,6 +572,18 @@ describe 'Issue Boards', feature: true, js: true do
end end
end end
context 'keyboard shortcuts' do
before do
visit namespace_project_board_path(project.namespace, project)
wait_for_vue_resource
end
it 'allows user to use keyboard shortcuts' do
find('.boards-list').native.send_keys('i')
expect(page).to have_content('New Issue')
end
end
context 'signed out user' do context 'signed out user' do
before do before do
logout logout
......
...@@ -4,12 +4,6 @@ feature 'test coverage badge' do ...@@ -4,12 +4,6 @@ feature 'test coverage badge' do
given!(:user) { create(:user) } given!(:user) { create(:user) }
given!(:project) { create(:project, :private) } given!(:project) { create(:project, :private) }
given!(:pipeline) do
create(:ci_pipeline, project: project,
ref: 'master',
sha: project.commit.id)
end
context 'when user has access to view badge' do context 'when user has access to view badge' do
background do background do
project.team << [user, :developer] project.team << [user, :developer]
...@@ -17,8 +11,10 @@ feature 'test coverage badge' do ...@@ -17,8 +11,10 @@ feature 'test coverage badge' do
end end
scenario 'user requests coverage badge image for pipeline' do scenario 'user requests coverage badge image for pipeline' do
create_job(coverage: 100, name: 'test:1') create_pipeline do |pipeline|
create_job(coverage: 90, name: 'test:2') create_build(pipeline, coverage: 100, name: 'test:1')
create_build(pipeline, coverage: 90, name: 'test:2')
end
show_test_coverage_badge show_test_coverage_badge
...@@ -26,9 +22,11 @@ feature 'test coverage badge' do ...@@ -26,9 +22,11 @@ feature 'test coverage badge' do
end end
scenario 'user requests coverage badge for specific job' do scenario 'user requests coverage badge for specific job' do
create_job(coverage: 50, name: 'test:1') create_pipeline do |pipeline|
create_job(coverage: 50, name: 'test:2') create_build(pipeline, coverage: 50, name: 'test:1')
create_job(coverage: 85, name: 'coverage') create_build(pipeline, coverage: 50, name: 'test:2')
create_build(pipeline, coverage: 85, name: 'coverage')
end
show_test_coverage_badge(job: 'coverage') show_test_coverage_badge(job: 'coverage')
...@@ -36,7 +34,9 @@ feature 'test coverage badge' do ...@@ -36,7 +34,9 @@ feature 'test coverage badge' do
end end
scenario 'user requests coverage badge for pipeline without coverage' do scenario 'user requests coverage badge for pipeline without coverage' do
create_job(coverage: nil, name: 'test') create_pipeline do |pipeline|
create_build(pipeline, coverage: nil, name: 'test')
end
show_test_coverage_badge show_test_coverage_badge
...@@ -54,10 +54,19 @@ feature 'test coverage badge' do ...@@ -54,10 +54,19 @@ feature 'test coverage badge' do
end end
end end
def create_job(coverage:, name:) def create_pipeline
create(:ci_build, name: name, opts = { project: project, ref: 'master', sha: project.commit.id }
coverage: coverage,
pipeline: pipeline) create(:ci_pipeline, opts).tap do |pipeline|
yield pipeline
pipeline.build_updated
end
end
def create_build(pipeline, coverage:, name:)
opts = { pipeline: pipeline, coverage: coverage, name: name }
create(:ci_build, :success, opts)
end end
def show_test_coverage_badge(job: nil) def show_test_coverage_badge(job: nil)
......
require 'spec_helper'
feature 'Delete branch', feature: true, js: true do
include WaitForAjax
let(:project) { create(:project) }
let(:user) { create(:user) }
before do
project.team << [user, :master]
login_as user
visit namespace_project_branches_path(project.namespace, project)
end
it 'destroys tooltip' do
first('.remove-row').hover
expect(page).to have_selector('.tooltip')
first('.remove-row').click
wait_for_ajax
expect(page).not_to have_selector('.tooltip')
end
end
...@@ -20,7 +20,7 @@ describe 'Branches', feature: true do ...@@ -20,7 +20,7 @@ describe 'Branches', feature: true do
describe 'Find branches' do describe 'Find branches' do
it 'shows filtered branches', js: true do it 'shows filtered branches', js: true do
visit namespace_project_branches_path(project.namespace, project, project.id) visit namespace_project_branches_path(project.namespace, project)
fill_in 'branch-search', with: 'fix' fill_in 'branch-search', with: 'fix'
find('#branch-search').native.send_keys(:enter) find('#branch-search').native.send_keys(:enter)
......
require 'spec_helper' require 'spec_helper'
include WaitForAjax
describe 'Cherry-pick Commits' do describe 'Cherry-pick Commits' do
let(:project) { create(:project) } let(:project) { create(:project) }
...@@ -8,12 +9,11 @@ describe 'Cherry-pick Commits' do ...@@ -8,12 +9,11 @@ describe 'Cherry-pick Commits' do
before do before do
login_as :user login_as :user
project.team << [@user, :master] project.team << [@user, :master]
visit namespace_project_commits_path(project.namespace, project, project.repository.root_ref, { limit: 5 }) visit namespace_project_commit_path(project.namespace, project, master_pickable_commit.id)
end end
context "I cherry-pick a commit" do context "I cherry-pick a commit" do
it do it do
visit namespace_project_commit_path(project.namespace, project, master_pickable_commit.id)
find("a[href='#modal-cherry-pick-commit']").click find("a[href='#modal-cherry-pick-commit']").click
expect(page).not_to have_content('v1.0.0') # Only branches, not tags expect(page).not_to have_content('v1.0.0') # Only branches, not tags
page.within('#modal-cherry-pick-commit') do page.within('#modal-cherry-pick-commit') do
...@@ -26,7 +26,6 @@ describe 'Cherry-pick Commits' do ...@@ -26,7 +26,6 @@ describe 'Cherry-pick Commits' do
context "I cherry-pick a merge commit" do context "I cherry-pick a merge commit" do
it do it do
visit namespace_project_commit_path(project.namespace, project, master_pickable_merge.id)
find("a[href='#modal-cherry-pick-commit']").click find("a[href='#modal-cherry-pick-commit']").click
page.within('#modal-cherry-pick-commit') do page.within('#modal-cherry-pick-commit') do
uncheck 'create_merge_request' uncheck 'create_merge_request'
...@@ -38,7 +37,6 @@ describe 'Cherry-pick Commits' do ...@@ -38,7 +37,6 @@ describe 'Cherry-pick Commits' do
context "I cherry-pick a commit that was previously cherry-picked" do context "I cherry-pick a commit that was previously cherry-picked" do
it do it do
visit namespace_project_commit_path(project.namespace, project, master_pickable_commit.id)
find("a[href='#modal-cherry-pick-commit']").click find("a[href='#modal-cherry-pick-commit']").click
page.within('#modal-cherry-pick-commit') do page.within('#modal-cherry-pick-commit') do
uncheck 'create_merge_request' uncheck 'create_merge_request'
...@@ -56,7 +54,6 @@ describe 'Cherry-pick Commits' do ...@@ -56,7 +54,6 @@ describe 'Cherry-pick Commits' do
context "I cherry-pick a commit in a new merge request" do context "I cherry-pick a commit in a new merge request" do
it do it do
visit namespace_project_commit_path(project.namespace, project, master_pickable_commit.id)
find("a[href='#modal-cherry-pick-commit']").click find("a[href='#modal-cherry-pick-commit']").click
page.within('#modal-cherry-pick-commit') do page.within('#modal-cherry-pick-commit') do
click_button 'Cherry-pick' click_button 'Cherry-pick'
...@@ -64,4 +61,28 @@ describe 'Cherry-pick Commits' do ...@@ -64,4 +61,28 @@ describe 'Cherry-pick Commits' do
expect(page).to have_content('The commit has been successfully cherry-picked. You can now submit a merge request to get this change into the original branch.') expect(page).to have_content('The commit has been successfully cherry-picked. You can now submit a merge request to get this change into the original branch.')
end end
end end
context "I cherry-pick a commit from a different branch", js: true do
it do
find('.commit-action-buttons a.dropdown-toggle').click
find(:css, "a[href='#modal-cherry-pick-commit']").click
page.within('#modal-cherry-pick-commit') do
click_button 'master'
end
wait_for_ajax
page.within('#modal-cherry-pick-commit .dropdown-menu .dropdown-content') do
click_link 'feature'
end
page.within('#modal-cherry-pick-commit') do
uncheck 'create_merge_request'
click_button 'Cherry-pick'
end
expect(page).to have_content('The commit has been successfully cherry-picked.')
end
end
end end
...@@ -51,6 +51,28 @@ describe MoveToProjectFinder do ...@@ -51,6 +51,28 @@ describe MoveToProjectFinder do
expect(subject.execute(project).to_a).to eq([other_reporter_project]) expect(subject.execute(project).to_a).to eq([other_reporter_project])
end end
it 'returns a page of projects ordered by id in descending order' do
stub_const 'MoveToProjectFinder::PAGE_SIZE', 2
reporter_project.team << [user, :reporter]
developer_project.team << [user, :developer]
master_project.team << [user, :master]
expect(subject.execute(project).to_a).to eq([master_project, developer_project])
end
it 'returns projects after the given offset id' do
stub_const 'MoveToProjectFinder::PAGE_SIZE', 2
reporter_project.team << [user, :reporter]
developer_project.team << [user, :developer]
master_project.team << [user, :master]
expect(subject.execute(project, search: nil, offset_id: master_project.id).to_a).to eq([developer_project, reporter_project])
expect(subject.execute(project, search: nil, offset_id: developer_project.id).to_a).to eq([reporter_project])
expect(subject.execute(project, search: nil, offset_id: reporter_project.id).to_a).to be_empty
end
end end
context 'search' do context 'search' do
......
require 'spec_helper'
describe IssuablesHelper do
let(:label) { build_stubbed(:label) }
let(:label2) { build_stubbed(:label) }
context 'label tooltip' do
it 'returns label text' do
expect(issuable_labels_tooltip([label])).to eq(label.title)
end
it 'returns label text' do
expect(issuable_labels_tooltip([label, label2], limit: 1)).to eq("#{label.title}, and 1 more")
end
end
end
.block.labels
.sidebar-collapsed-icon.js-sidebar-labels-tooltip
.title.hide-collapsed
%a.edit-link.pull-right{ href: "#" }
Edit
.selectbox.hide-collapsed{ style: "display: none;" }
.dropdown
%button.dropdown-menu-toggle.js-label-select.js-multiselect{ type: "button", data: { ability_name: "issue", field_name: "issue[label_names][]", issue_update: "/root/test/issues/2.json", labels: "/root/test/labels.json", project_id: "12", show_any: "true", show_no: "true", toggle: "dropdown" } }
%span.dropdown-toggle-text
Label
%i.fa.fa-chevron-down
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
.dropdown-page-one
.dropdown-content
.dropdown-loading
%i.fa.fa-spinner.fa-spin
//= require lib/utils/type_utility
//= require jquery
//= require bootstrap
//= require gl_dropdown
//= require select2
//= require jquery.nicescroll
//= require api
//= require create_label
//= require issuable_context
//= require users_select
//= require labels_select
(() => {
let saveLabelCount = 0;
describe('Issue dropdown sidebar', () => {
fixture.preload('issue_sidebar_label.html');
beforeEach(() => {
fixture.load('issue_sidebar_label.html');
new IssuableContext('{"id":1,"name":"Administrator","username":"root"}');
new LabelsSelect();
spyOn(jQuery, 'ajax').and.callFake((req) => {
const d = $.Deferred();
let LABELS_DATA = []
if (req.url === '/root/test/labels.json') {
for (let i = 0; i < 10; i++) {
LABELS_DATA.push({id: i, title: `test ${i}`, color: '#5CB85C'});
}
} else if (req.url === '/root/test/issues/2.json') {
let tmp = []
for (let i = 0; i < saveLabelCount; i++) {
tmp.push({id: i, title: `test ${i}`, color: '#5CB85C'});
}
LABELS_DATA = {labels: tmp};
}
d.resolve(LABELS_DATA);
return d.promise();
});
});
it('changes collapsed tooltip when changing labels when less than 5', (done) => {
saveLabelCount = 5;
$('.edit-link').get(0).click();
setTimeout(() => {
expect($('.dropdown-content a').length).toBe(10);
$('.dropdow-content a').each((i, $link) => {
if (i < 5) {
$link.get(0).click();
}
});
$('.edit-link').get(0).click();
setTimeout(() => {
expect($('.sidebar-collapsed-icon').attr('data-original-title')).toBe('test 0, test 1, test 2, test 3, test 4');
done();
}, 0);
}, 0);
});
it('changes collapsed tooltip when changing labels when more than 5', (done) => {
saveLabelCount = 6;
$('.edit-link').get(0).click();
setTimeout(() => {
expect($('.dropdown-content a').length).toBe(10);
$('.dropdow-content a').each((i, $link) => {
if (i < 5) {
$link.get(0).click();
}
});
$('.edit-link').get(0).click();
setTimeout(() => {
expect($('.sidebar-collapsed-icon').attr('data-original-title')).toBe('test 0, test 1, test 2, test 3, test 4, and 1 more');
done();
}, 0);
}, 0);
});
});
})();
...@@ -44,45 +44,49 @@ describe Gitlab::Badge::Coverage::Report do ...@@ -44,45 +44,49 @@ describe Gitlab::Badge::Coverage::Report do
end end
end end
context 'pipeline exists' do context 'when latest successful pipeline exists' do
let!(:pipeline) do before do
create(:ci_pipeline, project: project, create_pipeline do |pipeline|
sha: project.commit.id, create(:ci_build, :success, pipeline: pipeline, name: 'first', coverage: 40)
ref: 'master') create(:ci_build, :success, pipeline: pipeline, coverage: 60)
end end
context 'builds exist' do create_pipeline do |pipeline|
before do create(:ci_build, :failed, pipeline: pipeline, coverage: 10)
create(:ci_build, name: 'first', pipeline: pipeline, coverage: 40)
create(:ci_build, pipeline: pipeline, coverage: 60)
end end
end
context 'particular job specified' do context 'when particular job specified' do
let(:job_name) { 'first' } let(:job_name) { 'first' }
it 'returns coverage for the particular job' do it 'returns coverage for the particular job' do
expect(badge.status).to eq 40 expect(badge.status).to eq 40
end
end end
end
context 'particular job not specified' do context 'when particular job not specified' do
let(:job_name) { '' } let(:job_name) { '' }
it 'returns arithemetic mean for the pipeline' do
expect(badge.status).to eq 50
end
end
end
it 'returns arithemetic mean for the pipeline' do context 'when only failed pipeline exists' do
expect(badge.status).to eq 50 before do
end create_pipeline do |pipeline|
create(:ci_build, :failed, pipeline: pipeline, coverage: 10)
end end
end end
context 'builds do not exist' do it_behaves_like 'unknown coverage report'
it_behaves_like 'unknown coverage report'
context 'particular job specified' do context 'particular job specified' do
let(:job_name) { 'nonexistent' } let(:job_name) { 'nonexistent' }
it 'retruns nil' do it 'retruns nil' do
expect(badge.status).to be_nil expect(badge.status).to be_nil
end
end end
end end
end end
...@@ -90,4 +94,13 @@ describe Gitlab::Badge::Coverage::Report do ...@@ -90,4 +94,13 @@ describe Gitlab::Badge::Coverage::Report do
context 'pipeline does not exist' do context 'pipeline does not exist' do
it_behaves_like 'unknown coverage report' it_behaves_like 'unknown coverage report'
end end
def create_pipeline
opts = { project: project, sha: project.commit.id, ref: 'master' }
create(:ci_pipeline, opts).tap do |pipeline|
yield pipeline
pipeline.build_updated
end
end
end end
...@@ -719,6 +719,14 @@ describe Repository, models: true do ...@@ -719,6 +719,14 @@ describe Repository, models: true do
expect(merge_commit).to be_present expect(merge_commit).to be_present
expect(repository.blob_at(merge_commit.id, 'files/ruby/feature.rb')).to be_present expect(repository.blob_at(merge_commit.id, 'files/ruby/feature.rb')).to be_present
end end
it 'sets the `in_progress_merge_commit_sha` flag for the given merge request' do
merge_request = create(:merge_request, source_branch: 'feature', target_branch: 'master', source_project: project)
merge_commit_id = repository.merge(user, merge_request, commit_options)
repository.commit(merge_commit_id)
expect(merge_request.in_progress_merge_commit_sha).to eq(merge_commit_id)
end
end end
describe '#revert' do describe '#revert' do
......
...@@ -1113,6 +1113,46 @@ describe NotificationService, services: true do ...@@ -1113,6 +1113,46 @@ describe NotificationService, services: true do
end end
end end
describe 'GroupMember' do
describe '#decline_group_invite' do
let(:creator) { create(:user) }
let(:group) { create(:group) }
let(:member) { create(:user) }
before(:each) do
group.add_owner(creator)
group.add_developer(member, creator)
end
it do
group_member = group.members.first
expect do
notification.decline_group_invite(group_member)
end.to change { ActionMailer::Base.deliveries.size }.by(1)
end
end
end
describe 'ProjectMember' do
describe '#decline_group_invite' do
let(:project) { create(:project) }
let(:member) { create(:user) }
before(:each) do
project.team << [member, :developer, project.owner]
end
it do
project_member = project.members.first
expect do
notification.decline_project_invite(project_member)
end.to change { ActionMailer::Base.deliveries.size }.by(1)
end
end
end
def build_team(project) def build_team(project)
@u_watcher = create_global_setting_for(create(:user), :watch) @u_watcher = create_global_setting_for(create(:user), :watch)
@u_participating = create_global_setting_for(create(:user), :participating) @u_participating = create_global_setting_for(create(:user), :participating)
......
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
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