Commit a975e4cb authored by Alfredo Sumaran's avatar Alfredo Sumaran

Merge branch 'master' into merge-conflicts-editor-2

parents 86dcb79b d4feb781
...@@ -20,8 +20,8 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -20,8 +20,8 @@ Please view this file on the master branch, on stable branches it's out of date.
- Clarify documentation for Runners API (Gennady Trafimenkov) - Clarify documentation for Runners API (Gennady Trafimenkov)
- The instrumentation for Banzai::Renderer has been restored - The instrumentation for Banzai::Renderer has been restored
- Change user & group landing page routing from /u/:username to /:username - Change user & group landing page routing from /u/:username to /:username
- Prevent running GfmAutocomplete setup for each diff note !6569
- Added documentation for .gitattributes files - Added documentation for .gitattributes files
- Move Pipeline Metrics to separate worker
- AbstractReferenceFilter caches project_refs on RequestStore when active - AbstractReferenceFilter caches project_refs on RequestStore when active
- Replaced the check sign to arrow in the show build view. !6501 - Replaced the check sign to arrow in the show build view. !6501
- Add a /wip slash command to toggle the Work In Progress status of a merge request. !6259 (tbalthazar) - Add a /wip slash command to toggle the Work In Progress status of a merge request. !6259 (tbalthazar)
...@@ -40,7 +40,6 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -40,7 +40,6 @@ Please view this file on the master branch, on stable branches it's out of date.
- Update Gitlab Shell to fix some problems with moving projects between storages - Update Gitlab Shell to fix some problems with moving projects between storages
- Cache rendered markdown in the database, rather than Redis - Cache rendered markdown in the database, rather than Redis
- Avoid database queries on Banzai::ReferenceParser::BaseParser for nodes without references - Avoid database queries on Banzai::ReferenceParser::BaseParser for nodes without references
- Do not alter 'force_remove_source_branch' options on MergeRequest unless specified
- Simplify Mentionable concern instance methods - Simplify Mentionable concern instance methods
- API: Ability to retrieve version information (Robert Schilling) - API: Ability to retrieve version information (Robert Schilling)
- Fix permission for setting an issue's due date - Fix permission for setting an issue's due date
...@@ -57,6 +56,7 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -57,6 +56,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Added soft wrap button to repository file/blob editor - Added soft wrap button to repository file/blob editor
- Update namespace validation to forbid reserved names (.git and .atom) (Will Starms) - Update namespace validation to forbid reserved names (.git and .atom) (Will Starms)
- Show the time ago a merge request was deployed to an environment - Show the time ago a merge request was deployed to an environment
- Add RTL support to markdown renderer (Ebrahim Byagowi)
- Add word-wrap to issue title on issue and milestone boards (ClemMakesApps) - Add word-wrap to issue title on issue and milestone boards (ClemMakesApps)
- Fix todos page mobile viewport layout (ClemMakesApps) - Fix todos page mobile viewport layout (ClemMakesApps)
- Fix inconsistent highlighting of already selected activity nav-links (ClemMakesApps) - Fix inconsistent highlighting of already selected activity nav-links (ClemMakesApps)
...@@ -76,14 +76,12 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -76,14 +76,12 @@ Please view this file on the master branch, on stable branches it's out of date.
- Only update issuable labels if they have been changed - Only update issuable labels if they have been changed
- Take filters in account in issuable counters. !6496 - Take filters in account in issuable counters. !6496
- Use custom Ruby images to test builds (registry.dev.gitlab.org/gitlab/gitlab-build-images:*) - Use custom Ruby images to test builds (registry.dev.gitlab.org/gitlab/gitlab-build-images:*)
- Prevent flash alert text from being obscured when container is fluid
- Append issue template to existing description !6149 (Joseph Frazier) - Append issue template to existing description !6149 (Joseph Frazier)
- Trending projects now only show public projects and the list of projects is cached for a day - Trending projects now only show public projects and the list of projects is cached for a day
- Memoize Gitlab Shell's secret token (!6599, Justin DiPierro) - Memoize Gitlab Shell's secret token (!6599, Justin DiPierro)
- Revoke button in Applications Settings underlines on hover. - Revoke button in Applications Settings underlines on hover.
- Use higher size on Gitlab::Redis connection pool on Sidekiq servers - Use higher size on Gitlab::Redis connection pool on Sidekiq servers
- Add missing values to linter !6276 (Katarzyna Kobierska Ula Budziszewska) - Add missing values to linter !6276 (Katarzyna Kobierska Ula Budziszewska)
- Fix Long commit messages overflow viewport in file tree
- Revert avoid touching file system on Build#artifacts? - Revert avoid touching file system on Build#artifacts?
- Stop using a Redis lease when updating the project activity timestamp whenever a new event is created - Stop using a Redis lease when updating the project activity timestamp whenever a new event is created
- Add disabled delete button to protected branches (ClemMakesApps) - Add disabled delete button to protected branches (ClemMakesApps)
...@@ -124,8 +122,15 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -124,8 +122,15 @@ Please view this file on the master branch, on stable branches it's out of date.
## 8.12.7 ## 8.12.7
- Use gitlab-markup gem instead of github-markup to fix `.rst` file rendering. !6659 - Prevent running `GfmAutocomplete` setup for each diff note. !6569
- Fix GFM autocomplete setup being called several times - Fix long commit messages overflow viewport in file tree. !6573
- Use `gitlab-markup` gem instead of `github-markup` to fix `.rst` file rendering. !6659
- Prevent flash alert text from being obscured when container is fluid. !6694
- Fix due date being displayed as `NaN` in Safari. !6797
- Fix JS bug with select2 because of missing `data-field` attribute in select box. !6812
- Do not alter `force_remove_source_branch` options on MergeRequest unless specified. !6817
- Fix GFM autocomplete setup being called several times. !6840
- Handle case where deployment ref no longer exists. !6855
## 8.12.6 ## 8.12.6
......
...@@ -262,8 +262,6 @@ group :development do ...@@ -262,8 +262,6 @@ group :development do
# thin instead webrick # thin instead webrick
gem 'thin', '~> 1.7.0' gem 'thin', '~> 1.7.0'
gem 'activerecord_sane_schema_dumper', '0.2'
end end
group :development, :test do group :development, :test do
...@@ -310,6 +308,8 @@ group :development, :test do ...@@ -310,6 +308,8 @@ group :development, :test do
gem 'license_finder', '~> 2.1.0', require: false gem 'license_finder', '~> 2.1.0', require: false
gem 'knapsack', '~> 1.11.0' gem 'knapsack', '~> 1.11.0'
gem 'activerecord_sane_schema_dumper', '0.2'
end end
group :test do group :test do
......
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
case 'projects:milestones:new': case 'projects:milestones:new':
case 'projects:milestones:edit': case 'projects:milestones:edit':
new ZenMode(); new ZenMode();
new DueDateSelect(); new gl.DueDateSelectors();
new GLForm($('.milestone-form')); new GLForm($('.milestone-form'));
break; break;
case 'groups:milestones:new': case 'groups:milestones:new':
......
(function() {
this.DueDateSelect = (function() {
function DueDateSelect() {
var $datePicker, $dueDate, $loading;
// Milestone edit/new form
$datePicker = $('.datepicker');
if ($datePicker.length) {
$dueDate = $('#milestone_due_date');
$datePicker.datepicker({
dateFormat: 'yy-mm-dd',
onSelect: function(dateText, inst) {
return $dueDate.val(dateText);
}
}).datepicker('setDate', $.datepicker.parseDate('yy-mm-dd', $dueDate.val()));
}
$('.js-clear-due-date').on('click', function(e) {
e.preventDefault();
return $.datepicker._clearDate($datePicker);
});
// Issuable sidebar
$loading = $('.js-issuable-update .due_date').find('.block-loading').hide();
$('.js-due-date-select').each(function(i, dropdown) {
var $block, $dropdown, $dropdownParent, $selectbox, $sidebarValue, $value, $valueContent, abilityName, addDueDate, fieldName, issueUpdateURL;
$dropdown = $(dropdown);
$dropdownParent = $dropdown.closest('.dropdown');
$datePicker = $dropdownParent.find('.js-due-date-calendar');
$block = $dropdown.closest('.block');
$selectbox = $dropdown.closest('.selectbox');
$value = $block.find('.value');
$valueContent = $block.find('.value-content');
$sidebarValue = $('.js-due-date-sidebar-value', $block);
fieldName = $dropdown.data('field-name');
abilityName = $dropdown.data('ability-name');
issueUpdateURL = $dropdown.data('issue-update');
$dropdown.glDropdown({
hidden: function() {
$selectbox.hide();
return $value.css('display', '');
}
});
addDueDate = function(isDropdown) {
var data, date, mediumDate, value;
// Create the post date
value = $("input[name='" + fieldName + "']").val();
if (value !== '') {
date = new Date(value.replace(new RegExp('-', 'g'), ','));
mediumDate = $.datepicker.formatDate('M d, yy', date);
} else {
mediumDate = 'No due date';
}
data = {};
data[abilityName] = {};
data[abilityName].due_date = value;
return $.ajax({
type: 'PUT',
url: issueUpdateURL,
data: data,
dataType: 'json',
beforeSend: function() {
var cssClass;
$loading.fadeIn();
if (isDropdown) {
$dropdown.trigger('loading.gl.dropdown');
$selectbox.hide();
}
$value.css('display', '');
cssClass = Date.parse(mediumDate) ? 'bold' : 'no-value';
$valueContent.html("<span class='" + cssClass + "'>" + mediumDate + "</span>");
$sidebarValue.html(mediumDate);
if (value !== '') {
return $('.js-remove-due-date-holder').removeClass('hidden');
} else {
return $('.js-remove-due-date-holder').addClass('hidden');
}
}
}).done(function(data) {
if (isDropdown) {
$dropdown.trigger('loaded.gl.dropdown');
$dropdown.dropdown('toggle');
}
return $loading.fadeOut();
});
};
$block.on('click', '.js-remove-due-date', function(e) {
e.preventDefault();
$("input[name='" + fieldName + "']").val('');
return addDueDate(false);
});
return $datePicker.datepicker({
dateFormat: 'yy-mm-dd',
defaultDate: $("input[name='" + fieldName + "']").val(),
altField: "input[name='" + fieldName + "']",
onSelect: function() {
return addDueDate(true);
}
});
});
$(document).off('click', '.ui-datepicker-header a').on('click', '.ui-datepicker-header a', function(e) {
return e.stopImmediatePropagation();
});
}
return DueDateSelect;
})();
}).call(this);
(function(global) {
class DueDateSelect {
constructor({ $dropdown, $loading } = {}) {
const $dropdownParent = $dropdown.closest('.dropdown');
const $block = $dropdown.closest('.block');
this.$loading = $loading;
this.$dropdown = $dropdown;
this.$dropdownParent = $dropdownParent;
this.$datePicker = $dropdownParent.find('.js-due-date-calendar');
this.$block = $block;
this.$selectbox = $dropdown.closest('.selectbox');
this.$value = $block.find('.value');
this.$valueContent = $block.find('.value-content');
this.$sidebarValue = $('.js-due-date-sidebar-value', $block);
this.fieldName = $dropdown.data('field-name'),
this.abilityName = $dropdown.data('ability-name'),
this.issueUpdateURL = $dropdown.data('issue-update')
this.rawSelectedDate = null;
this.displayedDate = null;
this.datePayload = null;
this.initGlDropdown();
this.initRemoveDueDate();
this.initDatePicker();
this.initStopPropagation();
}
initGlDropdown() {
this.$dropdown.glDropdown({
hidden: () => {
this.$selectbox.hide();
this.$value.css('display', '');
}
});
}
initDatePicker() {
this.$datePicker.datepicker({
dateFormat: 'yy-mm-dd',
defaultDate: $("input[name='" + this.fieldName + "']").val(),
altField: "input[name='" + this.fieldName + "']",
onSelect: () => {
return this.saveDueDate(true);
}
});
}
initRemoveDueDate() {
this.$block.on('click', '.js-remove-due-date', (e) => {
e.preventDefault();
$("input[name='" + this.fieldName + "']").val('');
return this.saveDueDate(false);
});
}
initStopPropagation() {
$(document).off('click', '.ui-datepicker-header a').on('click', '.ui-datepicker-header a', (e) => {
return e.stopImmediatePropagation();
});
}
saveDueDate(isDropdown) {
this.parseSelectedDate();
this.prepSelectedDate();
this.submitSelectedDate(isDropdown);
}
parseSelectedDate() {
this.rawSelectedDate = $("input[name='" + this.fieldName + "']").val();
if (this.rawSelectedDate.length) {
let dateObj = new Date(this.rawSelectedDate);
this.displayedDate = $.datepicker.formatDate('M d, yy', dateObj);
} else {
this.displayedDate = 'No due date';
}
}
prepSelectedDate() {
const datePayload = {};
datePayload[this.abilityName] = {};
datePayload[this.abilityName].due_date = this.rawSelectedDate;
this.datePayload = datePayload;
}
submitSelectedDate(isDropdown) {
return $.ajax({
type: 'PUT',
url: this.issueUpdateURL,
data: this.datePayload,
dataType: 'json',
beforeSend: () => {
const selectedDateValue = this.datePayload[this.abilityName].due_date;
const displayedDateStyle = this.displayedDate !== 'No due date' ? 'bold' : 'no-value';
this.$loading.fadeIn();
if (isDropdown) {
this.$dropdown.trigger('loading.gl.dropdown');
this.$selectbox.hide();
}
this.$value.css('display', '');
this.$valueContent.html(`<span class='${displayedDateStyle}'>${this.displayedDate}</span>`);
this.$sidebarValue.html(this.displayedDate);
return selectedDateValue.length ?
$('.js-remove-due-date-holder').removeClass('hidden') :
$('.js-remove-due-date-holder').addClass('hidden');
}
}).done((data) => {
if (isDropdown) {
this.$dropdown.trigger('loaded.gl.dropdown');
this.$dropdown.dropdown('toggle');
}
return this.$loading.fadeOut();
});
}
}
class DueDateSelectors {
constructor() {
this.initMilestoneDueDate();
this.initIssuableSelect();
}
initMilestoneDueDate() {
const $datePicker = $('.datepicker');
if ($datePicker.length) {
const $dueDate = $('#milestone_due_date');
$datePicker.datepicker({
dateFormat: 'yy-mm-dd',
onSelect: (dateText, inst) => {
$dueDate.val(dateText);
}
}).datepicker('setDate', $.datepicker.parseDate('yy-mm-dd', $dueDate.val()));
}
$('.js-clear-due-date').on('click', (e) => {
e.preventDefault();
$.datepicker._clearDate($datePicker);
});
}
initIssuableSelect() {
const $loading = $('.js-issuable-update .due_date').find('.block-loading').hide();
$('.js-due-date-select').each((i, dropdown) => {
const $dropdown = $(dropdown);
new DueDateSelect({
$dropdown,
$loading
});
});
}
}
global.DueDateSelectors = DueDateSelectors;
})(window.gl || (window.gl = {}));
...@@ -90,6 +90,11 @@ ...@@ -90,6 +90,11 @@
border-left: 3px solid #e7e9ed; border-left: 3px solid #e7e9ed;
} }
blockquote:dir(rtl) {
border-left: 0;
border-right: 3px solid #e7e9ed;
}
blockquote p { blockquote p {
color: #7f8fa4 !important; color: #7f8fa4 !important;
font-size: inherit; font-size: inherit;
...@@ -112,6 +117,10 @@ ...@@ -112,6 +117,10 @@
} }
} }
table:dir(rtl) th {
text-align: right;
}
pre { pre {
margin: 12px 0; margin: 12px 0;
font-size: 13px; font-size: 13px;
...@@ -129,6 +138,10 @@ ...@@ -129,6 +138,10 @@
margin: 3px 0 3px 28px !important; margin: 3px 0 3px 28px !important;
} }
ul:dir(rtl), ol:dir(rtl) {
margin: 3px 28px 3px 0 !important;
}
li { li {
line-height: 1.6em; line-height: 1.6em;
} }
......
...@@ -49,6 +49,10 @@ module Ci ...@@ -49,6 +49,10 @@ module Ci
transition any => :canceled transition any => :canceled
end end
# IMPORTANT
# Do not add any operations to this state_machine
# Create a separate worker for each new operation
before_transition [:created, :pending] => :running do |pipeline| before_transition [:created, :pending] => :running do |pipeline|
pipeline.started_at = Time.now pipeline.started_at = Time.now
end end
...@@ -62,13 +66,11 @@ module Ci ...@@ -62,13 +66,11 @@ module Ci
end end
after_transition [:created, :pending] => :running do |pipeline| after_transition [:created, :pending] => :running do |pipeline|
MergeRequest::Metrics.where(merge_request_id: pipeline.merge_requests.map(&:id)). pipeline.run_after_commit { PipelineMetricsWorker.perform_async(id) }
update_all(latest_build_started_at: pipeline.started_at, latest_build_finished_at: nil)
end end
after_transition any => [:success] do |pipeline| after_transition any => [:success] do |pipeline|
MergeRequest::Metrics.where(merge_request_id: pipeline.merge_requests.map(&:id)). pipeline.run_after_commit { PipelineMetricsWorker.perform_async(id) }
update_all(latest_build_finished_at: pipeline.finished_at)
end end
after_transition [:created, :pending, :running] => :success do |pipeline| after_transition [:created, :pending, :running] => :success do |pipeline|
......
...@@ -171,5 +171,5 @@ ...@@ -171,5 +171,5 @@
new LabelsSelect(); new LabelsSelect();
new IssuableContext('#{escape_javascript(current_user.to_json(only: [:username, :id, :name]))}'); new IssuableContext('#{escape_javascript(current_user.to_json(only: [:username, :id, :name]))}');
new Subscription('.subscription') new Subscription('.subscription')
new DueDateSelect(); new gl.DueDateSelectors();
sidebar = new Sidebar(); sidebar = new Sidebar();
class PipelineMetricsWorker
include Sidekiq::Worker
sidekiq_options queue: :default
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id).try do |pipeline|
update_metrics_for_active_pipeline(pipeline) if pipeline.active?
update_metrics_for_succeeded_pipeline(pipeline) if pipeline.success?
end
end
private
def update_metrics_for_active_pipeline(pipeline)
metrics(pipeline).update_all(latest_build_started_at: pipeline.started_at, latest_build_finished_at: nil)
end
def update_metrics_for_succeeded_pipeline(pipeline)
metrics(pipeline).update_all(latest_build_started_at: pipeline.started_at, latest_build_finished_at: pipeline.finished_at)
end
def metrics(pipeline)
MergeRequest::Metrics.where(merge_request_id: merge_requests(pipeline))
end
def merge_requests(pipeline)
pipeline.merge_requests.map(&:id)
end
end
doc/raketasks/backup_hrz.png

8.7 KB | W: | H:

doc/raketasks/backup_hrz.png

31 KB | W: | H:

doc/raketasks/backup_hrz.png
doc/raketasks/backup_hrz.png
doc/raketasks/backup_hrz.png
doc/raketasks/backup_hrz.png
  • 2-up
  • Swipe
  • Onion skin
...@@ -21,7 +21,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps ...@@ -21,7 +21,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
expect(response_headers['Content-Type']).to have_content("application/atom+xml") expect(response_headers['Content-Type']).to have_content("application/atom+xml")
expect(body).to have_selector("title", text: "#{@project.name}:master commits") expect(body).to have_selector("title", text: "#{@project.name}:master commits")
expect(body).to have_selector("author email", text: commit.author_email) expect(body).to have_selector("author email", text: commit.author_email)
expect(body).to have_selector("entry summary", text: commit.description[0..10]) expect(body).to have_selector("entry summary", text: commit.description[0..10].delete("\r"))
end end
step 'I click on tag link' do step 'I click on tag link' do
......
...@@ -512,6 +512,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps ...@@ -512,6 +512,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should see new target branch changes' do step 'I should see new target branch changes' do
expect(page).to have_content 'Request to merge fix into feature' expect(page).to have_content 'Request to merge fix into feature'
expect(page).to have_content 'Target branch changed from merge-test to feature' expect(page).to have_content 'Target branch changed from merge-test to feature'
wait_for_ajax
end end
step 'I click on "Email Patches"' do step 'I click on "Email Patches"' do
......
module Banzai
module Filter
# HTML filter that sets dir="auto" for RTL languages support
class SetDirectionFilter < HTML::Pipeline::Filter
def call
# select these elements just on top level of the document
doc.xpath('p|h1|h2|h3|h4|h5|h6|ol|ul[not(@class="section-nav")]|blockquote|table').each do |el|
el['dir'] = 'auto'
end
doc
end
end
end
end
...@@ -25,7 +25,9 @@ module Banzai ...@@ -25,7 +25,9 @@ module Banzai
Filter::MilestoneReferenceFilter, Filter::MilestoneReferenceFilter,
Filter::TaskListFilter, Filter::TaskListFilter,
Filter::InlineDiffFilter Filter::InlineDiffFilter,
Filter::SetDirectionFilter
] ]
end end
......
...@@ -53,7 +53,7 @@ describe "User Feed", feature: true do ...@@ -53,7 +53,7 @@ describe "User Feed", feature: true do
end end
it 'has XHTML summaries in issue descriptions' do it 'has XHTML summaries in issue descriptions' do
expect(body).to match /we have a bug!<\/p>\n\n<hr ?\/>\n\n<p>I guess/ expect(body).to match /we have a bug!<\/p>\n\n<hr ?\/>\n\n<p dir="auto">I guess/
end end
it 'has XHTML summaries in notes' do it 'has XHTML summaries in notes' do
......
...@@ -24,7 +24,7 @@ describe Banzai::ObjectRenderer do ...@@ -24,7 +24,7 @@ describe Banzai::ObjectRenderer do
with(an_instance_of(Array)). with(an_instance_of(Array)).
and_call_original and_call_original
expect(object).to receive(:redacted_note_html=).with('<p>hello</p>') expect(object).to receive(:redacted_note_html=).with('<p dir="auto">hello</p>')
expect(object).to receive(:user_visible_reference_count=).with(0) expect(object).to receive(:user_visible_reference_count=).with(0)
renderer.render([object], :note) renderer.render([object], :note)
...@@ -92,10 +92,10 @@ describe Banzai::ObjectRenderer do ...@@ -92,10 +92,10 @@ describe Banzai::ObjectRenderer do
docs = renderer.render_attributes(objects, :note) docs = renderer.render_attributes(objects, :note)
expect(docs[0]).to be_an_instance_of(Nokogiri::HTML::DocumentFragment) expect(docs[0]).to be_an_instance_of(Nokogiri::HTML::DocumentFragment)
expect(docs[0].to_html).to eq('<p>hello</p>') expect(docs[0].to_html).to eq('<p dir="auto">hello</p>')
expect(docs[1]).to be_an_instance_of(Nokogiri::HTML::DocumentFragment) expect(docs[1]).to be_an_instance_of(Nokogiri::HTML::DocumentFragment)
expect(docs[1].to_html).to eq('<p>bye</p>') expect(docs[1].to_html).to eq('<p dir="auto">bye</p>')
end end
it 'returns when no objects to render' do it 'returns when no objects to render' do
......
...@@ -4,11 +4,11 @@ describe Banzai::Pipeline::DescriptionPipeline do ...@@ -4,11 +4,11 @@ describe Banzai::Pipeline::DescriptionPipeline do
def parse(html) def parse(html)
# When we pass HTML to Redcarpet, it gets wrapped in `p` tags... # When we pass HTML to Redcarpet, it gets wrapped in `p` tags...
# ...except when we pass it pre-wrapped text. Rabble rabble. # ...except when we pass it pre-wrapped text. Rabble rabble.
unwrap = !html.start_with?('<p>') unwrap = !html.start_with?('<p ')
output = described_class.to_html(html, project: spy) output = described_class.to_html(html, project: spy)
output.gsub!(%r{\A<p>(.*)</p>(.*)\z}, '\1\2') if unwrap output.gsub!(%r{\A<p dir="auto">(.*)</p>(.*)\z}, '\1\2') if unwrap
output output
end end
...@@ -27,11 +27,17 @@ describe Banzai::Pipeline::DescriptionPipeline do ...@@ -27,11 +27,17 @@ describe Banzai::Pipeline::DescriptionPipeline do
end end
end end
%w(b i strong em a ins del sup sub p).each do |elem| %w(b i strong em a ins del sup sub).each do |elem|
it "still allows '#{elem}' elements" do it "still allows '#{elem}' elements" do
exp = act = "<#{elem}>Description</#{elem}>" exp = act = "<#{elem}>Description</#{elem}>"
expect(parse(act).strip).to eq exp expect(parse(act).strip).to eq exp
end end
end end
it "still allows 'p' elements" do
exp = act = "<p dir=\"auto\">Description</p>"
expect(parse(act).strip).to eq exp
end
end end
...@@ -187,33 +187,24 @@ describe Ci::Pipeline, models: true do ...@@ -187,33 +187,24 @@ describe Ci::Pipeline, models: true do
end end
end end
describe "merge request metrics" do describe 'merge request metrics' do
let(:project) { FactoryGirl.create :project } let(:project) { FactoryGirl.create :project }
let(:pipeline) { FactoryGirl.create(:ci_empty_pipeline, status: 'created', project: project, ref: 'master', sha: project.repository.commit('master').id) } let(:pipeline) { FactoryGirl.create(:ci_empty_pipeline, status: 'created', project: project, ref: 'master', sha: project.repository.commit('master').id) }
let!(:merge_request) { create(:merge_request, source_project: project, source_branch: pipeline.ref) } let!(:merge_request) { create(:merge_request, source_project: project, source_branch: pipeline.ref) }
context 'when transitioning to running' do before do
it 'records the build start time' do expect(PipelineMetricsWorker).to receive(:perform_async).with(pipeline.id)
time = Time.now end
Timecop.freeze(time) { build.run }
expect(merge_request.reload.metrics.latest_build_started_at).to be_within(1.second).of(time)
end
it 'clears the build end time' do
build.run
expect(merge_request.reload.metrics.latest_build_finished_at).to be_nil context 'when transitioning to running' do
it 'schedules metrics workers' do
pipeline.run
end end
end end
context 'when transitioning to success' do context 'when transitioning to success' do
it 'records the build end time' do it 'schedules metrics workers' do
build.run pipeline.succeed
time = Time.now
Timecop.freeze(time) { build.success }
expect(merge_request.reload.metrics.latest_build_finished_at).to be_within(1.second).of(time)
end end
end end
end end
......
...@@ -64,7 +64,7 @@ describe CacheMarkdownField do ...@@ -64,7 +64,7 @@ describe CacheMarkdownField do
let(:html) { "<p><code>Foo</code></p>" } let(:html) { "<p><code>Foo</code></p>" }
let(:updated_markdown) { "`Bar`" } let(:updated_markdown) { "`Bar`" }
let(:updated_html) { "<p><code>Bar</code></p>" } let(:updated_html) { "<p dir=\"auto\"><code>Bar</code></p>" }
subject { ThingWithMarkdownFields.new(foo: markdown, foo_html: html) } subject { ThingWithMarkdownFields.new(foo: markdown, foo_html: html) }
......
...@@ -98,7 +98,9 @@ module TestEnv ...@@ -98,7 +98,9 @@ module TestEnv
def setup_gitlab_shell def setup_gitlab_shell
unless File.directory?(Gitlab.config.gitlab_shell.path) unless File.directory?(Gitlab.config.gitlab_shell.path)
`rake gitlab:shell:install` unless system('rake', 'gitlab:shell:install')
raise 'Can`t clone gitlab-shell'
end
end end
end end
......
require 'spec_helper'
describe PipelineMetricsWorker do
let(:project) { create(:project) }
let!(:merge_request) { create(:merge_request, source_project: project, source_branch: pipeline.ref) }
let(:pipeline) do
create(:ci_empty_pipeline,
status: status,
project: project,
ref: 'master',
sha: project.repository.commit('master').id,
started_at: 1.hour.ago,
finished_at: Time.now)
end
describe '#perform' do
subject { described_class.new.perform(pipeline.id) }
context 'when pipeline is running' do
let(:status) { 'running' }
it 'records the build start time' do
subject
expect(merge_request.reload.metrics.latest_build_started_at).to be_within(1.second).of(pipeline.started_at)
end
it 'clears the build end time' do
subject
expect(merge_request.reload.metrics.latest_build_finished_at).to be_nil
end
end
context 'when pipeline succeeded' do
let(:status) { 'success' }
it 'records the build end time' do
subject
expect(merge_request.reload.metrics.latest_build_finished_at).to be_within(1.second).of(pipeline.finished_at)
end
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