Commit c03f4141 authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-03-26

# Conflicts:
#	config/webpack.config.js

[ci skip]
parents b38f4bbf 391732a2
...@@ -1809,9 +1809,11 @@ export default class Notes { ...@@ -1809,9 +1809,11 @@ export default class Notes {
} }
} }
$closeBtn.text($closeBtn.data('originalText'));
/* eslint-disable promise/catch-or-return */ /* eslint-disable promise/catch-or-return */
// Make request to submit comment on server // Make request to submit comment on server
axios return axios
.post(`${formAction}?html=true`, formData) .post(`${formAction}?html=true`, formData)
.then(res => { .then(res => {
const note = res.data; const note = res.data;
...@@ -1928,8 +1930,6 @@ export default class Notes { ...@@ -1928,8 +1930,6 @@ export default class Notes {
this.reenableTargetFormSubmitButton(e); this.reenableTargetFormSubmitButton(e);
this.addNoteError($form); this.addNoteError($form);
}); });
return $closeBtn.text($closeBtn.data('originalText'));
} }
/** /**
......
<script> <script>
import { sprintf, s__ } from '~/locale';
import statusCodes from '../../lib/utils/http_status'; import statusCodes from '../../lib/utils/http_status';
import { bytesToMiB } from '../../lib/utils/number_utils'; import { bytesToMiB } from '../../lib/utils/number_utils';
import { backOff } from '../../lib/utils/common_utils'; import { backOff } from '../../lib/utils/common_utils';
...@@ -45,17 +46,28 @@ export default { ...@@ -45,17 +46,28 @@ export default {
shouldShowMetricsUnavailable() { shouldShowMetricsUnavailable() {
return !this.loadingMetrics && !this.hasMetrics && !this.loadFailed; return !this.loadingMetrics && !this.hasMetrics && !this.loadFailed;
}, },
memoryChangeType() { memoryChangeMessage() {
const messageProps = {
memoryFrom: this.memoryFrom,
memoryTo: this.memoryTo,
metricsLinkStart: `<a href="${this.metricsMonitoringUrl}">`,
metricsLinkEnd: '</a>',
emphasisStart: '<b>',
emphasisEnd: '</b>',
};
const memoryTo = Number(this.memoryTo); const memoryTo = Number(this.memoryTo);
const memoryFrom = Number(this.memoryFrom); const memoryFrom = Number(this.memoryFrom);
let memoryUsageMsg = '';
if (memoryTo > memoryFrom) { if (memoryTo > memoryFrom) {
return 'increased'; memoryUsageMsg = sprintf(s__('mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB'), messageProps, false);
} else if (memoryTo < memoryFrom) { } else if (memoryTo < memoryFrom) {
return 'decreased'; memoryUsageMsg = sprintf(s__('mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB'), messageProps, false);
} else {
memoryUsageMsg = sprintf(s__('mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB'), messageProps, false);
} }
return 'unchanged'; return memoryUsageMsg;
}, },
}, },
mounted() { mounted() {
...@@ -130,24 +142,22 @@ export default { ...@@ -130,24 +142,22 @@ export default {
<i <i
class="fa fa-spinner fa-spin usage-info-load-spinner" class="fa fa-spinner fa-spin usage-info-load-spinner"
aria-hidden="true"> aria-hidden="true">
</i>Loading deployment statistics </i>{{ s__('mrWidget|Loading deployment statistics') }}
</p> </p>
<p <p
v-if="shouldShowMemoryGraph" v-if="shouldShowMemoryGraph"
class="usage-info js-usage-info"> class="usage-info js-usage-info">
<a {{ memoryChangeMessage }}
:href="metricsMonitoringUrl"
>Memory</a> usage <b>{{ memoryChangeType }}</b> from {{ memoryFrom }}MB to {{ memoryTo }}MB
</p> </p>
<p <p
v-if="shouldShowLoadFailure" v-if="shouldShowLoadFailure"
class="usage-info js-usage-info usage-info-failed"> class="usage-info js-usage-info usage-info-failed">
Failed to load deployment statistics {{ s__('mrWidget|Failed to load deployment statistics') }}
</p> </p>
<p <p
v-if="shouldShowMetricsUnavailable" v-if="shouldShowMetricsUnavailable"
class="usage-info js-usage-info usage-info-unavailable"> class="usage-info js-usage-info usage-info-unavailable">
Deployment statistics are not available currently {{ s__('mrWidget|Deployment statistics are not available currently') }}
</p> </p>
<memory-graph <memory-graph
v-if="shouldShowMemoryGraph" v-if="shouldShowMemoryGraph"
......
#!/usr/bin/env ruby #!/usr/bin/env ruby
begin
load File.expand_path('../spring', __FILE__) # Remove this block when upgraded to rails 5.0.
rescue LoadError => e unless %w[1 true].include?(ENV["RAILS5"])
raise unless e.message.include?('spring') begin
load File.expand_path('../spring', __FILE__)
rescue LoadError => e
raise unless e.message.include?('spring')
end
end end
APP_PATH = File.expand_path('../../config/application', __FILE__)
APP_PATH = File.expand_path('../config/application', __dir__)
require_relative '../config/boot' require_relative '../config/boot'
require 'rails/commands' require 'rails/commands'
#!/usr/bin/env ruby #!/usr/bin/env ruby
begin
load File.expand_path('../spring', __FILE__) # Remove this block when upgraded to rails 5.0.
rescue LoadError => e unless %w[1 true].include?(ENV["RAILS5"])
raise unless e.message.include?('spring') begin
load File.expand_path('../spring', __FILE__)
rescue LoadError => e
raise unless e.message.include?('spring')
end
end end
require_relative '../config/boot' require_relative '../config/boot'
require 'rake' require 'rake'
Rake.application.run Rake.application.run
#!/usr/bin/env ruby #!/usr/bin/env ruby
require 'pathname'
def rails5?
%w[1 true].include?(ENV["RAILS5"])
end
require "pathname"
# path to your application root. # path to your application root.
APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) APP_ROOT = Pathname.new File.expand_path("../../", __FILE__)
if rails5?
def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
end
end
Dir.chdir APP_ROOT do Dir.chdir APP_ROOT do
# This script is a starting point to setup your application. # This script is a starting point to setup your application.
# Add necessary setup steps to this file: # Add necessary setup steps to this file:
puts "== Installing dependencies ==" puts "== Installing dependencies =="
system "gem install bundler --conservative"
system "bundle check || bundle install" if rails5?
system! "gem install bundler --conservative"
system("bundle check") || system!("bundle install")
else
system "gem install bundler --conservative"
system "bundle check || bundle install"
end
# puts "\n== Copying sample files ==" # puts "\n== Copying sample files =="
# unless File.exist?("config/database.yml") # unless File.exist?("config/database.yml")
# system "cp config/database.yml.sample config/database.yml" # cp "config/database.yml.sample", "config/database.yml"
# end # end
puts "\n== Preparing database ==" puts "\n== Preparing database =="
system "bin/rake db:reset"
if rails5?
system! "bin/rails db:setup"
else
system "bin/rake db:reset"
end
puts "\n== Removing old logs and tempfiles ==" puts "\n== Removing old logs and tempfiles =="
system "rm -f log/*"
system "rm -rf tmp/cache" if rails5?
system! "bin/rails log:clear tmp:clear"
else
system "rm -f log/*"
system "rm -rf tmp/cache"
end
puts "\n== Restarting application server ==" puts "\n== Restarting application server =="
system "touch tmp/restart.txt"
if rails5?
system! "bin/rails restart"
else
system "touch tmp/restart.txt"
end
end end
#!/usr/bin/env ruby
require 'pathname'
require 'fileutils'
include FileUtils
# path to your application root.
APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
end
chdir APP_ROOT do
# This script is a way to update your development environment automatically.
# Add necessary update steps to this file.
puts '== Installing dependencies =='
system! 'gem install bundler --conservative'
system('bundle check') || system!('bundle install')
puts "\n== Updating database =="
system! 'bin/rails db:migrate'
puts "\n== Removing old logs and tempfiles =="
system! 'bin/rails log:clear tmp:clear'
puts "\n== Restarting application server =="
system! 'bin/rails restart'
end
---
title: Added i18n support for the prometheus memory widget
merge_request: 17753
author:
type: other
---
title: Fix 500 error when trying to resolve non-ASCII conflicts in the editor
merge_request: 17962
author:
type: fixed
require 'rubygems' def rails5?
%w[1 true].include?(ENV["RAILS5"])
end
# Set up gems listed in the Gemfile. require 'rubygems' unless rails5?
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
gemfile = rails5? ? "Gemfile.rails5" : "Gemfile"
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../#{gemfile}", __dir__)
# Set up gems listed in the Gemfile.
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
# Load the rails application # Load the rails application
require File.expand_path('../application', __FILE__)
# Remove this condition when upgraded to rails 5.0.
if %w[1 true].include?(ENV["RAILS5"])
require_relative 'application'
else
require File.expand_path('../application', __FILE__)
end
# Initialize the rails application # Initialize the rails application
Rails.application.initialize! Rails.application.initialize!
...@@ -9,7 +9,11 @@ Rails.application.configure do ...@@ -9,7 +9,11 @@ Rails.application.configure do
config.action_controller.perform_caching = true config.action_controller.perform_caching = true
# Disable Rails's static asset server (Apache or nginx will already do this) # Disable Rails's static asset server (Apache or nginx will already do this)
config.serve_static_files = false if Gitlab.rails5?
config.public_file_server.enabled = false
else
config.serve_static_files = false
end
# Compress JavaScripts and CSS. # Compress JavaScripts and CSS.
config.assets.js_compressor = :uglifier config.assets.js_compressor = :uglifier
......
...@@ -18,7 +18,13 @@ Rails.application.configure do ...@@ -18,7 +18,13 @@ Rails.application.configure do
# Configure static asset server for tests with Cache-Control for performance # Configure static asset server for tests with Cache-Control for performance
config.assets.compile = false if ENV['CI'] config.assets.compile = false if ENV['CI']
config.serve_static_files = true
if Gitlab.rails5?
config.public_file_server.enabled = true
else
config.serve_static_files = true
end
config.static_cache_control = "public, max-age=3600" config.static_cache_control = "public, max-age=3600"
# Show full error reports and disable caching # Show full error reports and disable caching
......
# Remove this `if` condition when upgraded to rails 5.0.
# The body must be kept.
if Gitlab.rails5?
# Be sure to restart your server when you modify this file.
# ActiveSupport::Reloader.to_prepare do
# ApplicationController.renderer.defaults.merge!(
# http_host: 'example.org',
# https: false
# )
# end
end
# Remove this `if` condition when upgraded to rails 5.0.
# The body must be kept.
if Gitlab.rails5?
# Be sure to restart your server when you modify this file.
#
# This file contains migration options to ease your Rails 5.0 upgrade.
#
# Once upgraded flip defaults one by one to migrate to the new default.
#
# Read the Guide for Upgrading Ruby on Rails for more info on each option.
Rails.application.config.action_controller.raise_on_unfiltered_parameters = true
# Enable per-form CSRF tokens. Previous versions had false.
Rails.application.config.action_controller.per_form_csrf_tokens = false
# Enable origin-checking CSRF mitigation. Previous versions had false.
Rails.application.config.action_controller.forgery_protection_origin_check = false
# Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`.
# Previous versions had false.
ActiveSupport.to_time_preserves_timezone = false
# Require `belongs_to` associations by default. Previous versions had false.
Rails.application.config.active_record.belongs_to_required_by_default = false
# Do not halt callback chains when a callback returns false. Previous versions had true.
ActiveSupport.halt_callback_chains_on_return_false = true
end
%w(
.ruby-version
.rbenv-vars
tmp/restart.txt
tmp/caching-dev.txt
).each { |path| Spring.watch(path) }
...@@ -37,6 +37,7 @@ function generateEntries() { ...@@ -37,6 +37,7 @@ function generateEntries() {
} }
pageEntries.forEach(path => generateAutoEntries(path)); pageEntries.forEach(path => generateAutoEntries(path));
<<<<<<< HEAD
// EE-specific auto entries // EE-specific auto entries
const eePageEntries = glob.sync('pages/**/index.js', { const eePageEntries = glob.sync('pages/**/index.js', {
...@@ -44,6 +45,8 @@ function generateEntries() { ...@@ -44,6 +45,8 @@ function generateEntries() {
}); });
eePageEntries.forEach(path => generateAutoEntries(path, 'ee')); eePageEntries.forEach(path => generateAutoEntries(path, 'ee'));
watchAutoEntries.concat(path.join(ROOT_PATH, 'ee/app/assets/javascripts/pages/')); watchAutoEntries.concat(path.join(ROOT_PATH, 'ee/app/assets/javascripts/pages/'));
=======
>>>>>>> upstream/master
autoEntriesCount = Object.keys(autoEntries).length; autoEntriesCount = Object.keys(autoEntries).length;
...@@ -253,12 +256,15 @@ const config = { ...@@ -253,12 +256,15 @@ const config = {
vendor: path.join(ROOT_PATH, 'vendor/assets/javascripts'), vendor: path.join(ROOT_PATH, 'vendor/assets/javascripts'),
vue$: 'vue/dist/vue.esm.js', vue$: 'vue/dist/vue.esm.js',
spec: path.join(ROOT_PATH, 'spec/javascripts'), spec: path.join(ROOT_PATH, 'spec/javascripts'),
<<<<<<< HEAD
// EE-only // EE-only
ee: path.join(ROOT_PATH, 'ee/app/assets/javascripts'), ee: path.join(ROOT_PATH, 'ee/app/assets/javascripts'),
ee_empty_states: path.join(ROOT_PATH, 'ee/app/views/shared/empty_states'), ee_empty_states: path.join(ROOT_PATH, 'ee/app/views/shared/empty_states'),
ee_icons: path.join(ROOT_PATH, 'ee/app/views/shared/icons'), ee_icons: path.join(ROOT_PATH, 'ee/app/views/shared/icons'),
ee_images: path.join(ROOT_PATH, 'ee/app/assets/images'), ee_images: path.join(ROOT_PATH, 'ee/app/assets/images'),
=======
>>>>>>> upstream/master
}, },
}, },
......
...@@ -40,7 +40,10 @@ module Gitlab ...@@ -40,7 +40,10 @@ module Gitlab
# when there are no conflict files. # when there are no conflict files.
files.each(&:lines) files.each(&:lines)
files.any? files.any?
rescue Gitlab::Git::CommandError, Gitlab::Git::Conflict::Parser::UnresolvableError, Gitlab::Git::Conflict::Resolver::ConflictSideMissing rescue Gitlab::Git::CommandError,
Gitlab::Git::Conflict::Parser::UnresolvableError,
Gitlab::Git::Conflict::Resolver::ConflictSideMissing,
Gitlab::Git::Conflict::File::UnsupportedEncoding
false false
end end
cache_method :can_be_resolved_in_ui? cache_method :can_be_resolved_in_ui?
......
...@@ -2,17 +2,19 @@ module Gitlab ...@@ -2,17 +2,19 @@ module Gitlab
module Git module Git
module Conflict module Conflict
class File class File
UnsupportedEncoding = Class.new(StandardError)
attr_reader :their_path, :our_path, :our_mode, :repository, :commit_oid attr_reader :their_path, :our_path, :our_mode, :repository, :commit_oid
attr_accessor :content attr_accessor :raw_content
def initialize(repository, commit_oid, conflict, content) def initialize(repository, commit_oid, conflict, raw_content)
@repository = repository @repository = repository
@commit_oid = commit_oid @commit_oid = commit_oid
@their_path = conflict[:theirs][:path] @their_path = conflict[:theirs][:path]
@our_path = conflict[:ours][:path] @our_path = conflict[:ours][:path]
@our_mode = conflict[:ours][:mode] @our_mode = conflict[:ours][:mode]
@content = content @raw_content = raw_content
end end
def lines def lines
...@@ -29,6 +31,14 @@ module Gitlab ...@@ -29,6 +31,14 @@ module Gitlab
end end
end end
def content
@content ||= @raw_content.dup.force_encoding('UTF-8')
raise UnsupportedEncoding unless @content.valid_encoding?
@content
end
def type def type
lines unless @type lines unless @type
......
...@@ -4,7 +4,6 @@ module Gitlab ...@@ -4,7 +4,6 @@ module Gitlab
class Parser class Parser
UnresolvableError = Class.new(StandardError) UnresolvableError = Class.new(StandardError)
UnmergeableFile = Class.new(UnresolvableError) UnmergeableFile = Class.new(UnresolvableError)
UnsupportedEncoding = Class.new(UnresolvableError)
# Recoverable errors - the conflict can be resolved in an editor, but not with # Recoverable errors - the conflict can be resolved in an editor, but not with
# sections. # sections.
...@@ -75,10 +74,6 @@ module Gitlab ...@@ -75,10 +74,6 @@ module Gitlab
def validate_text!(text) def validate_text!(text)
raise UnmergeableFile if text.blank? # Typically a binary file raise UnmergeableFile if text.blank? # Typically a binary file
raise UnmergeableFile if text.length > 200.kilobytes raise UnmergeableFile if text.length > 200.kilobytes
text.force_encoding('UTF-8')
raise UnsupportedEncoding unless text.valid_encoding?
end end
def validate_delimiter!(condition) def validate_delimiter!(condition)
......
...@@ -17,7 +17,7 @@ module Gitlab ...@@ -17,7 +17,7 @@ module Gitlab
current_file = file_from_gitaly_header(gitaly_file.header) current_file = file_from_gitaly_header(gitaly_file.header)
else else
current_file.content << gitaly_file.content current_file.raw_content << gitaly_file.content
end end
end end
end end
......
...@@ -16,15 +16,15 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -16,15 +16,15 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
window.gl = window.gl || {}; window.gl = window.gl || {};
gl.utils = gl.utils || {}; gl.utils = gl.utils || {};
const htmlEscape = (comment) => { const htmlEscape = comment => {
const escapedString = comment.replace(/["&'<>]/g, (a) => { const escapedString = comment.replace(/["&'<>]/g, a => {
const escapedToken = { const escapedToken = {
'&': '&amp;', '&': '&amp;',
'<': '&lt;', '<': '&lt;',
'>': '&gt;', '>': '&gt;',
'"': '&quot;', '"': '&quot;',
"'": '&#x27;', "'": '&#x27;',
'`': '&#x60;' '`': '&#x60;',
}[a]; }[a];
return escapedToken; return escapedToken;
...@@ -39,7 +39,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -39,7 +39,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
var commentsTemplate = 'merge_requests/merge_request_with_comment.html.raw'; var commentsTemplate = 'merge_requests/merge_request_with_comment.html.raw';
preloadFixtures(commentsTemplate); preloadFixtures(commentsTemplate);
beforeEach(function () { beforeEach(function() {
loadFixtures(commentsTemplate); loadFixtures(commentsTemplate);
gl.utils.disableButtonIfEmptyField = _.noop; gl.utils.disableButtonIfEmptyField = _.noop;
window.project_uploads_path = 'http://test.host/uploads'; window.project_uploads_path = 'http://test.host/uploads';
...@@ -51,6 +51,17 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -51,6 +51,17 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
$('body').removeAttr('data-page'); $('body').removeAttr('data-page');
}); });
describe('addBinding', () => {
it('calls postComment when comment button is clicked', () => {
spyOn(Notes.prototype, 'postComment');
this.notes = new Notes('', []);
$('.js-comment-button').click();
expect(Notes.prototype.postComment).toHaveBeenCalled();
});
});
describe('task lists', function() { describe('task lists', function() {
let mock; let mock;
...@@ -58,7 +69,13 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -58,7 +69,13 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
spyOn(axios, 'patch').and.callThrough(); spyOn(axios, 'patch').and.callThrough();
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
mock.onPatch(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`).reply(200, {}); mock
.onPatch(
`${
gl.TEST_HOST
}/frontend-fixtures/merge-requests-project/merge_requests/1.json`,
)
.reply(200, {});
$('.js-comment-button').on('click', function(e) { $('.js-comment-button').on('click', function(e) {
e.preventDefault(); e.preventDefault();
...@@ -73,18 +90,27 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -73,18 +90,27 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
it('modifies the Markdown field', function() { it('modifies the Markdown field', function() {
const changeEvent = document.createEvent('HTMLEvents'); const changeEvent = document.createEvent('HTMLEvents');
changeEvent.initEvent('change', true, true); changeEvent.initEvent('change', true, true);
$('input[type=checkbox]').attr('checked', true)[1].dispatchEvent(changeEvent); $('input[type=checkbox]')
.attr('checked', true)[1]
.dispatchEvent(changeEvent);
expect($('.js-task-list-field.original-task-list').val()).toBe('- [x] Task List Item'); expect($('.js-task-list-field.original-task-list').val()).toBe(
'- [x] Task List Item',
);
}); });
it('submits an ajax request on tasklist:changed', function(done) { it('submits an ajax request on tasklist:changed', function(done) {
$('.js-task-list-container').trigger('tasklist:changed'); $('.js-task-list-container').trigger('tasklist:changed');
setTimeout(() => { setTimeout(() => {
expect(axios.patch).toHaveBeenCalledWith(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`, { expect(axios.patch).toHaveBeenCalledWith(
note: { note: '' }, `${
}); gl.TEST_HOST
}/frontend-fixtures/merge-requests-project/merge_requests/1.json`,
{
note: { note: '' },
},
);
done(); done();
}); });
}); });
...@@ -100,10 +126,10 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -100,10 +126,10 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
spyOn(this.notes, 'renderNote').and.stub(); spyOn(this.notes, 'renderNote').and.stub();
$(textarea).data('autosave', { $(textarea).data('autosave', {
reset: function() {} reset: function() {},
}); });
$('.js-comment-button').on('click', (e) => { $('.js-comment-button').on('click', e => {
const $form = $(this); const $form = $(this);
e.preventDefault(); e.preventDefault();
this.notes.addNote($form); this.notes.addNote($form);
...@@ -149,7 +175,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -149,7 +175,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
<div class="note-text">${sampleComment}</div> <div class="note-text">${sampleComment}</div>
</li>`, </li>`,
note: sampleComment, note: sampleComment,
valid: true valid: true,
}; };
$form = $('form.js-main-target-form'); $form = $('form.js-main-target-form');
$notesContainer = $('ul.main-notes-list'); $notesContainer = $('ul.main-notes-list');
...@@ -163,7 +189,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -163,7 +189,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
mock.restore(); mock.restore();
}); });
it('updates note and resets edit form', (done) => { it('updates note and resets edit form', done => {
spyOn(this.notes, 'revertNoteEditForm'); spyOn(this.notes, 'revertNoteEditForm');
spyOn(this.notes, 'setupNewNote'); spyOn(this.notes, 'setupNewNote');
...@@ -175,7 +201,9 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -175,7 +201,9 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
updatedNote.note = 'bar'; updatedNote.note = 'bar';
this.notes.updateNote(updatedNote, $targetNote); this.notes.updateNote(updatedNote, $targetNote);
expect(this.notes.revertNoteEditForm).toHaveBeenCalledWith($targetNote); expect(this.notes.revertNoteEditForm).toHaveBeenCalledWith(
$targetNote,
);
expect(this.notes.setupNewNote).toHaveBeenCalled(); expect(this.notes.setupNewNote).toHaveBeenCalled();
done(); done();
...@@ -231,17 +259,14 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -231,17 +259,14 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
note: 'heya', note: 'heya',
html: '<div>heya</div>', html: '<div>heya</div>',
}; };
$notesList = jasmine.createSpyObj('$notesList', [ $notesList = jasmine.createSpyObj('$notesList', ['find', 'append']);
'find',
'append',
]);
notes = jasmine.createSpyObj('notes', [ notes = jasmine.createSpyObj('notes', [
'setupNewNote', 'setupNewNote',
'refresh', 'refresh',
'collapseLongCommitList', 'collapseLongCommitList',
'updateNotesCount', 'updateNotesCount',
'putConflictEditWarningInPlace' 'putConflictEditWarningInPlace',
]); ]);
notes.taskList = jasmine.createSpyObj('tasklist', ['init']); notes.taskList = jasmine.createSpyObj('tasklist', ['init']);
notes.note_ids = []; notes.note_ids = [];
...@@ -258,7 +283,10 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -258,7 +283,10 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
Notes.isNewNote.and.returnValue(true); Notes.isNewNote.and.returnValue(true);
Notes.prototype.renderNote.call(notes, note, null, $notesList); Notes.prototype.renderNote.call(notes, note, null, $notesList);
expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.html, $notesList); expect(Notes.animateAppendNote).toHaveBeenCalledWith(
note.html,
$notesList,
);
}); });
}); });
...@@ -273,7 +301,10 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -273,7 +301,10 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
Notes.prototype.renderNote.call(notes, note, null, $notesList); Notes.prototype.renderNote.call(notes, note, null, $notesList);
expect(Notes.animateUpdateNote).toHaveBeenCalledWith(note.html, $note); expect(Notes.animateUpdateNote).toHaveBeenCalledWith(
note.html,
$note,
);
expect(notes.setupNewNote).toHaveBeenCalledWith($newNote); expect(notes.setupNewNote).toHaveBeenCalledWith($newNote);
}); });
...@@ -301,7 +332,10 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -301,7 +332,10 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
$notesList.find.and.returnValue($note); $notesList.find.and.returnValue($note);
Notes.prototype.renderNote.call(notes, note, null, $notesList); Notes.prototype.renderNote.call(notes, note, null, $notesList);
expect(notes.putConflictEditWarningInPlace).toHaveBeenCalledWith(note, $note); expect(notes.putConflictEditWarningInPlace).toHaveBeenCalledWith(
note,
$note,
);
}); });
}); });
}); });
...@@ -311,11 +345,11 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -311,11 +345,11 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
it('should consider same note text as the same', () => { it('should consider same note text as the same', () => {
const result = Notes.isUpdatedNote( const result = Notes.isUpdatedNote(
{ {
note: 'initial' note: 'initial',
}, },
$(`<div> $(`<div>
<div class="original-note-content">initial</div> <div class="original-note-content">initial</div>
</div>`) </div>`),
); );
expect(result).toEqual(false); expect(result).toEqual(false);
...@@ -324,11 +358,11 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -324,11 +358,11 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
it('should consider same note with trailing newline as the same', () => { it('should consider same note with trailing newline as the same', () => {
const result = Notes.isUpdatedNote( const result = Notes.isUpdatedNote(
{ {
note: 'initial\n' note: 'initial\n',
}, },
$(`<div> $(`<div>
<div class="original-note-content">initial\n</div> <div class="original-note-content">initial\n</div>
</div>`) </div>`),
); );
expect(result).toEqual(false); expect(result).toEqual(false);
...@@ -337,11 +371,11 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -337,11 +371,11 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
it('should consider different notes as different', () => { it('should consider different notes as different', () => {
const result = Notes.isUpdatedNote( const result = Notes.isUpdatedNote(
{ {
note: 'foo' note: 'foo',
}, },
$(`<div> $(`<div>
<div class="original-note-content">bar</div> <div class="original-note-content">bar</div>
</div>`) </div>`),
); );
expect(result).toEqual(true); expect(result).toEqual(true);
...@@ -397,7 +431,10 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -397,7 +431,10 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
it('should call Notes.animateAppendNote', () => { it('should call Notes.animateAppendNote', () => {
Notes.prototype.renderDiscussionNote.call(notes, note, $form); Notes.prototype.renderDiscussionNote.call(notes, note, $form);
expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.discussion_html, $('.main-notes-list')); expect(Notes.animateAppendNote).toHaveBeenCalledWith(
note.discussion_html,
$('.main-notes-list'),
);
}); });
it('should append to row selected with line_code', () => { it('should append to row selected with line_code', () => {
...@@ -428,7 +465,10 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -428,7 +465,10 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
}); });
it('should call Notes.animateAppendNote', () => { it('should call Notes.animateAppendNote', () => {
expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.html, discussionContainer); expect(Notes.animateAppendNote).toHaveBeenCalledWith(
note.html,
discussionContainer,
);
}); });
}); });
}); });
...@@ -461,9 +501,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -461,9 +501,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
beforeEach(() => { beforeEach(() => {
noteHTML = '<div></div>'; noteHTML = '<div></div>';
$note = jasmine.createSpyObj('$note', [ $note = jasmine.createSpyObj('$note', ['replaceWith']);
'replaceWith'
]);
$updatedNote = Notes.animateUpdateNote(noteHTML, $note); $updatedNote = Notes.animateUpdateNote(noteHTML, $note);
}); });
...@@ -501,7 +539,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -501,7 +539,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
<div class="note-text">${sampleComment}</div> <div class="note-text">${sampleComment}</div>
</li>`, </li>`,
note: sampleComment, note: sampleComment,
valid: true valid: true,
}; };
let $form; let $form;
let $notesContainer; let $notesContainer;
...@@ -534,10 +572,12 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -534,10 +572,12 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
mockNotesPost(); mockNotesPost();
$('.js-comment-button').click(); $('.js-comment-button').click();
expect($notesContainer.find('.note.being-posted').length > 0).toEqual(true); expect($notesContainer.find('.note.being-posted').length > 0).toEqual(
true,
);
}); });
it('should remove placeholder note when new comment is done posting', (done) => { it('should remove placeholder note when new comment is done posting', done => {
mockNotesPost(); mockNotesPost();
$('.js-comment-button').click(); $('.js-comment-button').click();
...@@ -549,33 +589,44 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -549,33 +589,44 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
}); });
}); });
it('should disable the submit button when comment button is clicked', (done) => { describe('postComment', () => {
expect($form.find('.js-comment-submit-button').is(':disabled')).toEqual(false); it('disables the submit button', done => {
const $submitButton = $form.find('.js-comment-submit-button');
mockNotesPost(); expect($submitButton).not.toBeDisabled();
$('.js-comment-button').click(); const dummyEvent = {
expect($form.find('.js-comment-submit-button').is(':disabled')).toEqual(true); preventDefault() {},
target: $submitButton,
setTimeout(() => { };
expect($form.find('.js-comment-submit-button').is(':disabled')).toEqual(false); mock.onPost(NOTES_POST_PATH).replyOnce(() => {
expect($submitButton).toBeDisabled();
return [200, note];
});
done(); this.notes
.postComment(dummyEvent)
.then(() => {
expect($submitButton).not.toBeDisabled();
})
.then(done)
.catch(done.fail);
}); });
}); });
it('should show actual note element when new comment is done posting', (done) => { it('should show actual note element when new comment is done posting', done => {
mockNotesPost(); mockNotesPost();
$('.js-comment-button').click(); $('.js-comment-button').click();
setTimeout(() => { setTimeout(() => {
expect($notesContainer.find(`#note_${note.id}`).length > 0).toEqual(true); expect($notesContainer.find(`#note_${note.id}`).length > 0).toEqual(
true,
);
done(); done();
}); });
}); });
it('should reset Form when new comment is done posting', (done) => { it('should reset Form when new comment is done posting', done => {
mockNotesPost(); mockNotesPost();
$('.js-comment-button').click(); $('.js-comment-button').click();
...@@ -587,19 +638,24 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -587,19 +638,24 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
}); });
}); });
it('should show flash error message when new comment failed to be posted', (done) => { it('should show flash error message when new comment failed to be posted', done => {
mockNotesPostError(); mockNotesPostError();
$('.js-comment-button').click(); $('.js-comment-button').click();
setTimeout(() => { setTimeout(() => {
expect($notesContainer.parent().find('.flash-container .flash-text').is(':visible')).toEqual(true); expect(
$notesContainer
.parent()
.find('.flash-container .flash-text')
.is(':visible'),
).toEqual(true);
done(); done();
}); });
}); });
it('should show flash error message when comment failed to be updated', (done) => { it('should show flash error message when comment failed to be updated', done => {
mockNotesPost(); mockNotesPost();
$('.js-comment-button').click(); $('.js-comment-button').click();
...@@ -620,7 +676,12 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -620,7 +676,12 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
.then(() => { .then(() => {
const $updatedNoteEl = $notesContainer.find(`#note_${note.id}`); const $updatedNoteEl = $notesContainer.find(`#note_${note.id}`);
expect($updatedNoteEl.hasClass('.being-posted')).toEqual(false); // Remove being-posted visuals expect($updatedNoteEl.hasClass('.being-posted')).toEqual(false); // Remove being-posted visuals
expect($updatedNoteEl.find('.note-text').text().trim()).toEqual(sampleComment); // See if comment reverted back to original expect(
$updatedNoteEl
.find('.note-text')
.text()
.trim(),
).toEqual(sampleComment); // See if comment reverted back to original
expect($('.flash-container').is(':visible')).toEqual(true); // Flash error message shown expect($('.flash-container').is(':visible')).toEqual(true); // Flash error message shown
done(); done();
...@@ -634,12 +695,12 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -634,12 +695,12 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
const note = { const note = {
commands_changes: { commands_changes: {
assignee_id: 1, assignee_id: 1,
emoji_award: '100' emoji_award: '100',
}, },
errors: { errors: {
commands_only: ['Commands applied'] commands_only: ['Commands applied'],
}, },
valid: false valid: false,
}; };
let $form; let $form;
let $notesContainer; let $notesContainer;
...@@ -654,12 +715,12 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -654,12 +715,12 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
window.gon.current_user_fullname = 'Administrator'; window.gon.current_user_fullname = 'Administrator';
gl.awardsHandler = { gl.awardsHandler = {
addAwardToEmojiBar: () => {}, addAwardToEmojiBar: () => {},
scrollToAwards: () => {} scrollToAwards: () => {},
}; };
gl.GfmAutoComplete = { gl.GfmAutoComplete = {
dataSources: { dataSources: {
commands: '/root/test-project/autocomplete_sources/commands' commands: '/root/test-project/autocomplete_sources/commands',
} },
}; };
$form = $('form.js-main-target-form'); $form = $('form.js-main-target-form');
$notesContainer = $('ul.main-notes-list'); $notesContainer = $('ul.main-notes-list');
...@@ -670,14 +731,18 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -670,14 +731,18 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
mock.restore(); mock.restore();
}); });
it('should remove slash command placeholder when comment with slash commands is done posting', (done) => { it('should remove slash command placeholder when comment with slash commands is done posting', done => {
spyOn(gl.awardsHandler, 'addAwardToEmojiBar').and.callThrough(); spyOn(gl.awardsHandler, 'addAwardToEmojiBar').and.callThrough();
$('.js-comment-button').click(); $('.js-comment-button').click();
expect($notesContainer.find('.system-note.being-posted').length).toEqual(1); // Placeholder shown expect(
$notesContainer.find('.system-note.being-posted').length,
).toEqual(1); // Placeholder shown
setTimeout(() => { setTimeout(() => {
expect($notesContainer.find('.system-note.being-posted').length).toEqual(0); // Placeholder removed expect(
$notesContainer.find('.system-note.being-posted').length,
).toEqual(0); // Placeholder removed
done(); done();
}); });
}); });
...@@ -692,7 +757,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -692,7 +757,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
<div class="note-text">${sampleComment}</div> <div class="note-text">${sampleComment}</div>
</li>`, </li>`,
note: sampleComment, note: sampleComment,
valid: true valid: true,
}; };
let $form; let $form;
let $notesContainer; let $notesContainer;
...@@ -714,7 +779,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -714,7 +779,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
mock.restore(); mock.restore();
}); });
it('should not render a script tag', (done) => { it('should not render a script tag', done => {
$('.js-comment-button').click(); $('.js-comment-button').click();
setTimeout(() => { setTimeout(() => {
...@@ -723,8 +788,15 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -723,8 +788,15 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
$noteEl.find('textarea.js-note-text').html(updatedComment); $noteEl.find('textarea.js-note-text').html(updatedComment);
$noteEl.find('.js-comment-save-button').click(); $noteEl.find('.js-comment-save-button').click();
const $updatedNoteEl = $notesContainer.find(`#note_${note.id}`).find('.js-task-list-container'); const $updatedNoteEl = $notesContainer
expect($updatedNoteEl.find('.note-text').text().trim()).toEqual(''); .find(`#note_${note.id}`)
.find('.js-task-list-container');
expect(
$updatedNoteEl
.find('.note-text')
.text()
.trim(),
).toEqual('');
done(); done();
}); });
...@@ -744,7 +816,9 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -744,7 +816,9 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
it('should return form metadata object from form reference', () => { it('should return form metadata object from form reference', () => {
$form.find('textarea.js-note-text').val(sampleComment); $form.find('textarea.js-note-text').val(sampleComment);
const { formData, formContent, formAction } = this.notes.getFormData($form); const { formData, formContent, formAction } = this.notes.getFormData(
$form,
);
expect(formData.indexOf(sampleComment) > -1).toBe(true); expect(formData.indexOf(sampleComment) > -1).toBe(true);
expect(formContent).toEqual(sampleComment); expect(formContent).toEqual(sampleComment);
...@@ -760,7 +834,9 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -760,7 +834,9 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
const { formContent } = this.notes.getFormData($form); const { formContent } = this.notes.getFormData($form);
expect(_.escape).toHaveBeenCalledWith(sampleComment); expect(_.escape).toHaveBeenCalledWith(sampleComment);
expect(formContent).toEqual('&lt;script&gt;alert(&quot;Boom!&quot;);&lt;/script&gt;'); expect(formContent).toEqual(
'&lt;script&gt;alert(&quot;Boom!&quot;);&lt;/script&gt;',
);
}); });
}); });
...@@ -770,7 +846,8 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -770,7 +846,8 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
}); });
it('should return true when comment begins with a quick action', () => { it('should return true when comment begins with a quick action', () => {
const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this'; const sampleComment =
'/wip\n/milestone %1.0\n/merge\n/unassign Merging this';
const hasQuickActions = this.notes.hasQuickActions(sampleComment); const hasQuickActions = this.notes.hasQuickActions(sampleComment);
expect(hasQuickActions).toBeTruthy(); expect(hasQuickActions).toBeTruthy();
...@@ -794,7 +871,8 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -794,7 +871,8 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
describe('stripQuickActions', () => { describe('stripQuickActions', () => {
it('should strip quick actions from the comment which begins with a quick action', () => { it('should strip quick actions from the comment which begins with a quick action', () => {
this.notes = new Notes(); this.notes = new Notes();
const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this'; const sampleComment =
'/wip\n/milestone %1.0\n/merge\n/unassign Merging this';
const stripedComment = this.notes.stripQuickActions(sampleComment); const stripedComment = this.notes.stripQuickActions(sampleComment);
expect(stripedComment).toBe(''); expect(stripedComment).toBe('');
...@@ -802,7 +880,8 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -802,7 +880,8 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
it('should strip quick actions from the comment but leaves plain comment if it is present', () => { it('should strip quick actions from the comment but leaves plain comment if it is present', () => {
this.notes = new Notes(); this.notes = new Notes();
const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign\nMerging this'; const sampleComment =
'/wip\n/milestone %1.0\n/merge\n/unassign\nMerging this';
const stripedComment = this.notes.stripQuickActions(sampleComment); const stripedComment = this.notes.stripQuickActions(sampleComment);
expect(stripedComment).toBe('Merging this'); expect(stripedComment).toBe('Merging this');
...@@ -810,7 +889,8 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -810,7 +889,8 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
it('should NOT strip string that has slashes within', () => { it('should NOT strip string that has slashes within', () => {
this.notes = new Notes(); this.notes = new Notes();
const sampleComment = 'http://127.0.0.1:3000/root/gitlab-shell/issues/1'; const sampleComment =
'http://127.0.0.1:3000/root/gitlab-shell/issues/1';
const stripedComment = this.notes.stripQuickActions(sampleComment); const stripedComment = this.notes.stripQuickActions(sampleComment);
expect(stripedComment).toBe(sampleComment); expect(stripedComment).toBe(sampleComment);
...@@ -821,7 +901,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -821,7 +901,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
const availableQuickActions = [ const availableQuickActions = [
{ name: 'close', description: 'Close this issue', params: [] }, { name: 'close', description: 'Close this issue', params: [] },
{ name: 'title', description: 'Change title', params: [{}] }, { name: 'title', description: 'Change title', params: [{}] },
{ name: 'estimate', description: 'Set time estimate', params: [{}] } { name: 'estimate', description: 'Set time estimate', params: [{}] },
]; ];
beforeEach(() => { beforeEach(() => {
...@@ -830,17 +910,29 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -830,17 +910,29 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
it('should return executing quick action description when note has single quick action', () => { it('should return executing quick action description when note has single quick action', () => {
const sampleComment = '/close'; const sampleComment = '/close';
expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe('Applying command to close this issue'); expect(
this.notes.getQuickActionDescription(
sampleComment,
availableQuickActions,
),
).toBe('Applying command to close this issue');
}); });
it('should return generic multiple quick action description when note has multiple quick actions', () => { it('should return generic multiple quick action description when note has multiple quick actions', () => {
const sampleComment = '/close\n/title [Duplicate] Issue foobar'; const sampleComment = '/close\n/title [Duplicate] Issue foobar';
expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe('Applying multiple commands'); expect(
this.notes.getQuickActionDescription(
sampleComment,
availableQuickActions,
),
).toBe('Applying multiple commands');
}); });
it('should return generic quick action description when available quick actions list is not populated', () => { it('should return generic quick action description when available quick actions list is not populated', () => {
const sampleComment = '/close\n/title [Duplicate] Issue foobar'; const sampleComment = '/close\n/title [Duplicate] Issue foobar';
expect(this.notes.getQuickActionDescription(sampleComment)).toBe('Applying command'); expect(this.notes.getQuickActionDescription(sampleComment)).toBe(
'Applying command',
);
}); });
}); });
...@@ -870,14 +962,35 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -870,14 +962,35 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
expect($tempNote.attr('id')).toEqual(uniqueId); expect($tempNote.attr('id')).toEqual(uniqueId);
expect($tempNote.hasClass('being-posted')).toBeTruthy(); expect($tempNote.hasClass('being-posted')).toBeTruthy();
expect($tempNote.hasClass('fade-in-half')).toBeTruthy(); expect($tempNote.hasClass('fade-in-half')).toBeTruthy();
$tempNote.find('.timeline-icon > a, .note-header-info > a').each(function() { $tempNote
expect($(this).attr('href')).toEqual(`/${currentUsername}`); .find('.timeline-icon > a, .note-header-info > a')
}); .each(function() {
expect($tempNote.find('.timeline-icon .avatar').attr('src')).toEqual(currentUserAvatar); expect($(this).attr('href')).toEqual(`/${currentUsername}`);
expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeFalsy(); });
expect($tempNoteHeader.find('.hidden-xs').text().trim()).toEqual(currentUserFullname); expect($tempNote.find('.timeline-icon .avatar').attr('src')).toEqual(
expect($tempNoteHeader.find('.note-headline-light').text().trim()).toEqual(`@${currentUsername}`); currentUserAvatar,
expect($tempNote.find('.note-body .note-text p').text().trim()).toEqual(sampleComment); );
expect(
$tempNote.find('.timeline-content').hasClass('discussion'),
).toBeFalsy();
expect(
$tempNoteHeader
.find('.hidden-xs')
.text()
.trim(),
).toEqual(currentUserFullname);
expect(
$tempNoteHeader
.find('.note-headline-light')
.text()
.trim(),
).toEqual(`@${currentUsername}`);
expect(
$tempNote
.find('.note-body .note-text p')
.text()
.trim(),
).toEqual(sampleComment);
}); });
it('should return constructed placeholder element for discussion note based on form contents', () => { it('should return constructed placeholder element for discussion note based on form contents', () => {
...@@ -886,11 +999,13 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -886,11 +999,13 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
uniqueId, uniqueId,
isDiscussionNote: true, isDiscussionNote: true,
currentUsername, currentUsername,
currentUserFullname currentUserFullname,
}); });
expect($tempNote.prop('nodeName')).toEqual('LI'); expect($tempNote.prop('nodeName')).toEqual('LI');
expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeTruthy(); expect(
$tempNote.find('.timeline-content').hasClass('discussion'),
).toBeTruthy();
}); });
it('should return a escaped user name', () => { it('should return a escaped user name', () => {
...@@ -904,7 +1019,12 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -904,7 +1019,12 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
currentUserAvatar, currentUserAvatar,
}); });
const $tempNoteHeader = $tempNote.find('.note-header'); const $tempNoteHeader = $tempNote.find('.note-header');
expect($tempNoteHeader.find('.hidden-xs').text().trim()).toEqual('Foo &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;'); expect(
$tempNoteHeader
.find('.hidden-xs')
.text()
.trim(),
).toEqual('Foo &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;');
}); });
}); });
...@@ -927,7 +1047,12 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -927,7 +1047,12 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
expect($tempNote.attr('id')).toEqual(uniqueId); expect($tempNote.attr('id')).toEqual(uniqueId);
expect($tempNote.hasClass('being-posted')).toBeTruthy(); expect($tempNote.hasClass('being-posted')).toBeTruthy();
expect($tempNote.hasClass('fade-in-half')).toBeTruthy(); expect($tempNote.hasClass('fade-in-half')).toBeTruthy();
expect($tempNote.find('.timeline-content i').text().trim()).toEqual(sampleCommandDescription); expect(
$tempNote
.find('.timeline-content i')
.text()
.trim(),
).toEqual(sampleCommandDescription);
}); });
}); });
...@@ -937,7 +1062,11 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -937,7 +1062,11 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
}); });
it('shows a flash message', () => { it('shows a flash message', () => {
this.notes.addFlash('Error message', FLASH_TYPE_ALERT, this.notes.parentTimeline.get(0)); this.notes.addFlash(
'Error message',
FLASH_TYPE_ALERT,
this.notes.parentTimeline.get(0),
);
expect($('.flash-alert').is(':visible')).toBeTruthy(); expect($('.flash-alert').is(':visible')).toBeTruthy();
}); });
...@@ -950,7 +1079,11 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -950,7 +1079,11 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
}); });
it('hides visible flash message', () => { it('hides visible flash message', () => {
this.notes.addFlash('Error message 1', FLASH_TYPE_ALERT, this.notes.parentTimeline.get(0)); this.notes.addFlash(
'Error message 1',
FLASH_TYPE_ALERT,
this.notes.parentTimeline.get(0),
);
this.notes.clearFlash(); this.notes.clearFlash();
...@@ -958,4 +1091,4 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; ...@@ -958,4 +1091,4 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
}); });
}); });
}); });
}).call(window); }.call(window));
...@@ -23,9 +23,7 @@ const metricsMockData = { ...@@ -23,9 +23,7 @@ const metricsMockData = {
memory_values: [ memory_values: [
{ {
metric: {}, metric: {},
values: [ values: [[1493716685, '4.30859375']],
[1493716685, '4.30859375'],
],
}, },
], ],
}, },
...@@ -53,7 +51,8 @@ const createComponent = () => { ...@@ -53,7 +51,8 @@ const createComponent = () => {
const messages = { const messages = {
loadingMetrics: 'Loading deployment statistics', loadingMetrics: 'Loading deployment statistics',
hasMetrics: 'Memory usage unchanged from 0MB to 0MB', hasMetrics:
'<a href="/root/acets-review-apps/environments/15/metrics"> Memory </a> usage is <b> unchanged </b> at 0MB',
loadFailed: 'Failed to load deployment statistics', loadFailed: 'Failed to load deployment statistics',
metricsUnavailable: 'Deployment statistics are not available currently', metricsUnavailable: 'Deployment statistics are not available currently',
}; };
...@@ -92,26 +91,26 @@ describe('MemoryUsage', () => { ...@@ -92,26 +91,26 @@ describe('MemoryUsage', () => {
}); });
describe('computed', () => { describe('computed', () => {
describe('memoryChangeType', () => { describe('memoryChangeMessage', () => {
it('should return "increased" if memoryFrom value is less than memoryTo value', () => { it('should contain "increased" if memoryFrom value is less than memoryTo value', () => {
vm.memoryFrom = 4.28; vm.memoryFrom = 4.28;
vm.memoryTo = 9.13; vm.memoryTo = 9.13;
expect(vm.memoryChangeType).toEqual('increased'); expect(vm.memoryChangeMessage.indexOf('increased')).not.toEqual('-1');
}); });
it('should return "decreased" if memoryFrom value is less than memoryTo value', () => { it('should contain "decreased" if memoryFrom value is less than memoryTo value', () => {
vm.memoryFrom = 9.13; vm.memoryFrom = 9.13;
vm.memoryTo = 4.28; vm.memoryTo = 4.28;
expect(vm.memoryChangeType).toEqual('decreased'); expect(vm.memoryChangeMessage.indexOf('decreased')).not.toEqual('-1');
}); });
it('should return "unchanged" if memoryFrom value equal to memoryTo value', () => { it('should contain "unchanged" if memoryFrom value equal to memoryTo value', () => {
vm.memoryFrom = 1; vm.memoryFrom = 1;
vm.memoryTo = 1; vm.memoryTo = 1;
expect(vm.memoryChangeType).toEqual('unchanged'); expect(vm.memoryChangeMessage.indexOf('unchanged')).not.toEqual('-1');
}); });
}); });
}); });
...@@ -130,7 +129,13 @@ describe('MemoryUsage', () => { ...@@ -130,7 +129,13 @@ describe('MemoryUsage', () => {
describe('computeGraphData', () => { describe('computeGraphData', () => {
it('should populate sparkline graph', () => { it('should populate sparkline graph', () => {
vm.computeGraphData(metrics, deployment_time); vm.computeGraphData(metrics, deployment_time);
const { hasMetrics, memoryMetrics, deploymentTime, memoryFrom, memoryTo } = vm; const {
hasMetrics,
memoryMetrics,
deploymentTime,
memoryFrom,
memoryTo,
} = vm;
expect(hasMetrics).toBeTruthy(); expect(hasMetrics).toBeTruthy();
expect(memoryMetrics.length > 0).toBeTruthy(); expect(memoryMetrics.length > 0).toBeTruthy();
...@@ -141,20 +146,26 @@ describe('MemoryUsage', () => { ...@@ -141,20 +146,26 @@ describe('MemoryUsage', () => {
}); });
describe('loadMetrics', () => { describe('loadMetrics', () => {
const returnServicePromise = () => new Promise((resolve) => { const returnServicePromise = () =>
resolve({ new Promise(resolve => {
data: metricsMockData, resolve({
data: metricsMockData,
});
}); });
});
it('should load metrics data using MRWidgetService', (done) => { it('should load metrics data using MRWidgetService', done => {
spyOn(MRWidgetService, 'fetchMetrics').and.returnValue(returnServicePromise(true)); spyOn(MRWidgetService, 'fetchMetrics').and.returnValue(
returnServicePromise(true),
);
spyOn(vm, 'computeGraphData'); spyOn(vm, 'computeGraphData');
vm.loadMetrics(); vm.loadMetrics();
setTimeout(() => { setTimeout(() => {
expect(MRWidgetService.fetchMetrics).toHaveBeenCalledWith(url); expect(MRWidgetService.fetchMetrics).toHaveBeenCalledWith(url);
expect(vm.computeGraphData).toHaveBeenCalledWith(metrics, deployment_time); expect(vm.computeGraphData).toHaveBeenCalledWith(
metrics,
deployment_time,
);
done(); done();
}, 333); }, 333);
}); });
...@@ -167,51 +178,67 @@ describe('MemoryUsage', () => { ...@@ -167,51 +178,67 @@ describe('MemoryUsage', () => {
expect(el.querySelector('.js-usage-info')).toBeDefined(); expect(el.querySelector('.js-usage-info')).toBeDefined();
}); });
it('should show loading metrics message while metrics are being loaded', (done) => { it('should show loading metrics message while metrics are being loaded', done => {
vm.loadingMetrics = true; vm.loadingMetrics = true;
vm.hasMetrics = false; vm.hasMetrics = false;
vm.loadFailed = false; vm.loadFailed = false;
Vue.nextTick(() => { Vue.nextTick(() => {
expect(el.querySelector('.js-usage-info.usage-info-loading')).toBeDefined(); expect(
expect(el.querySelector('.js-usage-info .usage-info-load-spinner')).toBeDefined(); el.querySelector('.js-usage-info.usage-info-loading'),
expect(el.querySelector('.js-usage-info').innerText).toContain(messages.loadingMetrics); ).toBeDefined();
expect(
el.querySelector('.js-usage-info .usage-info-load-spinner'),
).toBeDefined();
expect(el.querySelector('.js-usage-info').innerText).toContain(
messages.loadingMetrics,
);
done(); done();
}); });
}); });
it('should show deployment memory usage when metrics are loaded', (done) => { it('should show deployment memory usage when metrics are loaded', done => {
vm.loadingMetrics = false; vm.loadingMetrics = false;
vm.hasMetrics = true; vm.hasMetrics = true;
vm.loadFailed = false; vm.loadFailed = false;
Vue.nextTick(() => { Vue.nextTick(() => {
expect(el.querySelector('.memory-graph-container')).toBeDefined(); expect(el.querySelector('.memory-graph-container')).toBeDefined();
expect(el.querySelector('.js-usage-info').innerText).toContain(messages.hasMetrics); expect(el.querySelector('.js-usage-info').innerText).toContain(
messages.hasMetrics,
);
done(); done();
}); });
}); });
it('should show failure message when metrics loading failed', (done) => { it('should show failure message when metrics loading failed', done => {
vm.loadingMetrics = false; vm.loadingMetrics = false;
vm.hasMetrics = false; vm.hasMetrics = false;
vm.loadFailed = true; vm.loadFailed = true;
Vue.nextTick(() => { Vue.nextTick(() => {
expect(el.querySelector('.js-usage-info.usage-info-failed')).toBeDefined(); expect(
expect(el.querySelector('.js-usage-info').innerText).toContain(messages.loadFailed); el.querySelector('.js-usage-info.usage-info-failed'),
).toBeDefined();
expect(el.querySelector('.js-usage-info').innerText).toContain(
messages.loadFailed,
);
done(); done();
}); });
}); });
it('should show metrics unavailable message when metrics loading failed', (done) => { it('should show metrics unavailable message when metrics loading failed', done => {
vm.loadingMetrics = false; vm.loadingMetrics = false;
vm.hasMetrics = false; vm.hasMetrics = false;
vm.loadFailed = false; vm.loadFailed = false;
Vue.nextTick(() => { Vue.nextTick(() => {
expect(el.querySelector('.js-usage-info.usage-info-unavailable')).toBeDefined(); expect(
expect(el.querySelector('.js-usage-info').innerText).toContain(messages.metricsUnavailable); el.querySelector('.js-usage-info.usage-info-unavailable'),
).toBeDefined();
expect(el.querySelector('.js-usage-info').innerText).toContain(
messages.metricsUnavailable,
);
done(); done();
}); });
}); });
......
# coding: utf-8
require 'spec_helper'
describe Gitlab::Git::Conflict::File do
let(:conflict) { { theirs: { path: 'foo', mode: 33188 }, ours: { path: 'foo', mode: 33188 } } }
let(:invalid_content) { described_class.new(nil, nil, conflict, "a\xC4\xFC".force_encoding(Encoding::ASCII_8BIT)) }
let(:valid_content) { described_class.new(nil, nil, conflict, "Espa\xC3\xB1a".force_encoding(Encoding::ASCII_8BIT)) }
describe '#lines' do
context 'when the content contains non-UTF-8 characters' do
it 'raises UnsupportedEncoding' do
expect { invalid_content.lines }
.to raise_error(described_class::UnsupportedEncoding)
end
end
context 'when the content can be converted to UTF-8' do
it 'sets lines to the lines' do
expect(valid_content.lines).to eq([{
full_line: 'España',
type: nil,
line_obj_index: 0,
line_old: 1,
line_new: 1
}])
end
it 'sets the type to text' do
expect(valid_content.type).to eq('text')
end
end
end
describe '#content' do
context 'when the content contains non-UTF-8 characters' do
it 'raises UnsupportedEncoding' do
expect { invalid_content.content }
.to raise_error(described_class::UnsupportedEncoding)
end
end
context 'when the content can be converted to UTF-8' do
it 'returns a valid UTF-8 string' do
expect(valid_content.content).to eq('España')
expect(valid_content.content).to be_valid_encoding
expect(valid_content.content.encoding).to eq(Encoding::UTF_8)
end
end
end
end
...@@ -212,13 +212,6 @@ CONFLICT ...@@ -212,13 +212,6 @@ CONFLICT
.not_to raise_error .not_to raise_error
end end
end end
context 'when the file contains non-UTF-8 characters' do
it 'raises UnsupportedEncoding' do
expect { parse_text("a\xC4\xFC".force_encoding(Encoding::ASCII_8BIT)) }
.to raise_error(Gitlab::Git::Conflict::Parser::UnsupportedEncoding)
end
end
end end
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