summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md17
-rw-r--r--Gemfile4
-rw-r--r--app/assets/javascripts/dispatcher.js.es62
-rw-r--r--app/assets/javascripts/due_date_select.js107
-rw-r--r--app/assets/javascripts/due_date_select.js.es6161
-rw-r--r--app/assets/stylesheets/framework/typography.scss13
-rw-r--r--app/models/ci/pipeline.rb10
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml2
-rw-r--r--app/workers/pipeline_metrics_worker.rb30
-rw-r--r--doc/raketasks/backup_hrz.pngbin8907 -> 31784 bytes
-rw-r--r--features/steps/project/commits/commits.rb2
-rw-r--r--features/steps/project/merge_requests.rb1
-rw-r--r--lib/banzai/filter/set_direction_filter.rb15
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb4
-rw-r--r--spec/features/atom/users_spec.rb2
-rw-r--r--spec/lib/banzai/object_renderer_spec.rb6
-rw-r--r--spec/lib/banzai/pipeline/description_pipeline_spec.rb12
-rw-r--r--spec/models/ci/pipeline_spec.rb27
-rw-r--r--spec/models/concerns/cache_markdown_field_spec.rb2
-rw-r--r--spec/support/test_env.rb4
-rw-r--r--spec/workers/pipeline_metrics_worker_spec.rb46
21 files changed, 317 insertions, 150 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9702f177278..54791fcb20f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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)
- The instrumentation for Banzai::Renderer has been restored
- 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
+ - Move Pipeline Metrics to separate worker
- AbstractReferenceFilter caches project_refs on RequestStore when active
- 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)
@@ -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
- Cache rendered markdown in the database, rather than Redis
- 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
- API: Ability to retrieve version information (Robert Schilling)
- 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.
- Added soft wrap button to repository file/blob editor
- Update namespace validation to forbid reserved names (.git and .atom) (Will Starms)
- 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)
- Fix todos page mobile viewport layout (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.
- Only update issuable labels if they have been changed
- Take filters in account in issuable counters. !6496
- 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)
- 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)
- Revoke button in Applications Settings underlines on hover.
- Use higher size on Gitlab::Redis connection pool on Sidekiq servers
- 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?
- 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)
@@ -124,8 +122,15 @@ Please view this file on the master branch, on stable branches it's out of date.
## 8.12.7
- - Use gitlab-markup gem instead of github-markup to fix `.rst` file rendering. !6659
- - Fix GFM autocomplete setup being called several times
+ - Prevent running `GfmAutocomplete` setup for each diff note. !6569
+ - 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
diff --git a/Gemfile b/Gemfile
index 5f754c1b66f..05166b6a828 100644
--- a/Gemfile
+++ b/Gemfile
@@ -262,8 +262,6 @@ group :development do
# thin instead webrick
gem 'thin', '~> 1.7.0'
-
- gem 'activerecord_sane_schema_dumper', '0.2'
end
group :development, :test do
@@ -310,6 +308,8 @@ group :development, :test do
gem 'license_finder', '~> 2.1.0', require: false
gem 'knapsack', '~> 1.11.0'
+
+ gem 'activerecord_sane_schema_dumper', '0.2'
end
group :test do
diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6
index 7545fae8fbf..73691f40c74 100644
--- a/app/assets/javascripts/dispatcher.js.es6
+++ b/app/assets/javascripts/dispatcher.js.es6
@@ -50,7 +50,7 @@
case 'projects:milestones:new':
case 'projects:milestones:edit':
new ZenMode();
- new DueDateSelect();
+ new gl.DueDateSelectors();
new GLForm($('.milestone-form'));
break;
case 'groups:milestones:new':
diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js
deleted file mode 100644
index bf68b7e3a9b..00000000000
--- a/app/assets/javascripts/due_date_select.js
+++ /dev/null
@@ -1,107 +0,0 @@
-(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);
diff --git a/app/assets/javascripts/due_date_select.js.es6 b/app/assets/javascripts/due_date_select.js.es6
new file mode 100644
index 00000000000..41925fcc8e3
--- /dev/null
+++ b/app/assets/javascripts/due_date_select.js.es6
@@ -0,0 +1,161 @@
+(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 = {}));
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 8df0067fac1..287653beac5 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -90,6 +90,11 @@
border-left: 3px solid #e7e9ed;
}
+ blockquote:dir(rtl) {
+ border-left: 0;
+ border-right: 3px solid #e7e9ed;
+ }
+
blockquote p {
color: #7f8fa4 !important;
font-size: inherit;
@@ -112,6 +117,10 @@
}
}
+ table:dir(rtl) th {
+ text-align: right;
+ }
+
pre {
margin: 12px 0;
font-size: 13px;
@@ -129,6 +138,10 @@
margin: 3px 0 3px 28px !important;
}
+ ul:dir(rtl), ol:dir(rtl) {
+ margin: 3px 28px 3px 0 !important;
+ }
+
li {
line-height: 1.6em;
}
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 4fdb5fef4fb..c7b9d6cc223 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -49,6 +49,10 @@ module Ci
transition any => :canceled
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|
pipeline.started_at = Time.now
end
@@ -62,13 +66,11 @@ module Ci
end
after_transition [:created, :pending] => :running do |pipeline|
- MergeRequest::Metrics.where(merge_request_id: pipeline.merge_requests.map(&:id)).
- update_all(latest_build_started_at: pipeline.started_at, latest_build_finished_at: nil)
+ pipeline.run_after_commit { PipelineMetricsWorker.perform_async(id) }
end
after_transition any => [:success] do |pipeline|
- MergeRequest::Metrics.where(merge_request_id: pipeline.merge_requests.map(&:id)).
- update_all(latest_build_finished_at: pipeline.finished_at)
+ pipeline.run_after_commit { PipelineMetricsWorker.perform_async(id) }
end
after_transition [:created, :pending, :running] => :success do |pipeline|
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index f8059988038..ba9f0c27661 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -171,5 +171,5 @@
new LabelsSelect();
new IssuableContext('#{escape_javascript(current_user.to_json(only: [:username, :id, :name]))}');
new Subscription('.subscription')
- new DueDateSelect();
+ new gl.DueDateSelectors();
sidebar = new Sidebar();
diff --git a/app/workers/pipeline_metrics_worker.rb b/app/workers/pipeline_metrics_worker.rb
new file mode 100644
index 00000000000..7bb92df3bbd
--- /dev/null
+++ b/app/workers/pipeline_metrics_worker.rb
@@ -0,0 +1,30 @@
+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
diff --git a/doc/raketasks/backup_hrz.png b/doc/raketasks/backup_hrz.png
index 42084717ebe..287587609a1 100644
--- a/doc/raketasks/backup_hrz.png
+++ b/doc/raketasks/backup_hrz.png
Binary files differ
diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb
index b08912de25f..244306e8464 100644
--- a/features/steps/project/commits/commits.rb
+++ b/features/steps/project/commits/commits.rb
@@ -21,7 +21,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
expect(response_headers['Content-Type']).to have_content("application/atom+xml")
expect(body).to have_selector("title", text: "#{@project.name}:master commits")
expect(body).to have_selector("author email", text: commit.author_email)
- expect(body).to have_selector("entry summary", text: commit.description[0..10])
+ expect(body).to have_selector("entry summary", text: commit.description[0..10].delete("\r"))
end
step 'I click on tag link' do
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index de065dffbc2..44346d99f44 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -512,6 +512,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
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 'Target branch changed from merge-test to feature'
+ wait_for_ajax
end
step 'I click on "Email Patches"' do
diff --git a/lib/banzai/filter/set_direction_filter.rb b/lib/banzai/filter/set_direction_filter.rb
new file mode 100644
index 00000000000..c2976aeb7c6
--- /dev/null
+++ b/lib/banzai/filter/set_direction_filter.rb
@@ -0,0 +1,15 @@
+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
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index 8d94b199c66..5da2d0b008c 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -25,7 +25,9 @@ module Banzai
Filter::MilestoneReferenceFilter,
Filter::TaskListFilter,
- Filter::InlineDiffFilter
+ Filter::InlineDiffFilter,
+
+ Filter::SetDirectionFilter
]
end
diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb
index a8833194421..f8c3ccb416b 100644
--- a/spec/features/atom/users_spec.rb
+++ b/spec/features/atom/users_spec.rb
@@ -53,7 +53,7 @@ describe "User Feed", feature: true do
end
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
it 'has XHTML summaries in notes' do
diff --git a/spec/lib/banzai/object_renderer_spec.rb b/spec/lib/banzai/object_renderer_spec.rb
index 90da78a67dd..6bcda87c999 100644
--- a/spec/lib/banzai/object_renderer_spec.rb
+++ b/spec/lib/banzai/object_renderer_spec.rb
@@ -24,7 +24,7 @@ describe Banzai::ObjectRenderer do
with(an_instance_of(Array)).
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)
renderer.render([object], :note)
@@ -92,10 +92,10 @@ describe Banzai::ObjectRenderer do
docs = renderer.render_attributes(objects, :note)
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_html).to eq('<p>bye</p>')
+ expect(docs[1].to_html).to eq('<p dir="auto">bye</p>')
end
it 'returns when no objects to render' do
diff --git a/spec/lib/banzai/pipeline/description_pipeline_spec.rb b/spec/lib/banzai/pipeline/description_pipeline_spec.rb
index 76f42071810..8cce1b96698 100644
--- a/spec/lib/banzai/pipeline/description_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/description_pipeline_spec.rb
@@ -4,11 +4,11 @@ describe Banzai::Pipeline::DescriptionPipeline do
def parse(html)
# When we pass HTML to Redcarpet, it gets wrapped in `p` tags...
# ...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.gsub!(%r{\A<p>(.*)</p>(.*)\z}, '\1\2') if unwrap
+ output.gsub!(%r{\A<p dir="auto">(.*)</p>(.*)\z}, '\1\2') if unwrap
output
end
@@ -27,11 +27,17 @@ describe Banzai::Pipeline::DescriptionPipeline do
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
exp = act = "<#{elem}>Description</#{elem}>"
expect(parse(act).strip).to eq exp
end
end
+
+ it "still allows 'p' elements" do
+ exp = act = "<p dir=\"auto\">Description</p>"
+
+ expect(parse(act).strip).to eq exp
+ end
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 550a890797e..163c0b5c516 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -187,33 +187,24 @@ describe Ci::Pipeline, models: true do
end
end
- describe "merge request metrics" do
+ describe 'merge request metrics' do
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!(:merge_request) { create(:merge_request, source_project: project, source_branch: pipeline.ref) }
- context 'when transitioning to running' do
- it 'records the build start time' do
- time = Time.now
- 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
+ before do
+ expect(PipelineMetricsWorker).to receive(:perform_async).with(pipeline.id)
+ end
- 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
context 'when transitioning to success' do
- it 'records the build end time' do
- build.run
- time = Time.now
- Timecop.freeze(time) { build.success }
-
- expect(merge_request.reload.metrics.latest_build_finished_at).to be_within(1.second).of(time)
+ it 'schedules metrics workers' do
+ pipeline.succeed
end
end
end
diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb
index 15cd3a7ed70..2e3702f7520 100644
--- a/spec/models/concerns/cache_markdown_field_spec.rb
+++ b/spec/models/concerns/cache_markdown_field_spec.rb
@@ -64,7 +64,7 @@ describe CacheMarkdownField do
let(:html) { "<p><code>Foo</code></p>" }
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) }
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index 2e3fd5118ef..c79975d8667 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -98,7 +98,9 @@ module TestEnv
def setup_gitlab_shell
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
diff --git a/spec/workers/pipeline_metrics_worker_spec.rb b/spec/workers/pipeline_metrics_worker_spec.rb
new file mode 100644
index 00000000000..232478c9735
--- /dev/null
+++ b/spec/workers/pipeline_metrics_worker_spec.rb
@@ -0,0 +1,46 @@
+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