Commit 9c8edcd6 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent bc898829
/* eslint-disable no-param-reassign, prefer-template, no-void, consistent-return */ /* eslint-disable no-param-reassign, no-void, consistent-return */
import AccessorUtilities from './lib/utils/accessor'; import AccessorUtilities from './lib/utils/accessor';
...@@ -10,7 +10,7 @@ export default class Autosave { ...@@ -10,7 +10,7 @@ export default class Autosave {
if (key.join != null) { if (key.join != null) {
key = key.join('/'); key = key.join('/');
} }
this.key = 'autosave/' + key; this.key = `autosave/${key}`;
this.field.data('autosave', this); this.field.data('autosave', this);
this.restore(); this.restore();
this.field.on('input', () => this.save()); this.field.on('input', () => this.save());
......
...@@ -26,7 +26,7 @@ $.fn.requiresInput = function requiresInput() { ...@@ -26,7 +26,7 @@ $.fn.requiresInput = function requiresInput() {
const values = _.map($(fieldSelector, $form), field => field.value); const values = _.map($(fieldSelector, $form), field => field.value);
// Disable the button if any required fields are empty // Disable the button if any required fields are empty
if (values.length && _.any(values, _.isEmpty)) { if (values.length && _.some(values, _.isEmpty)) {
$button.disable(); $button.disable();
} else { } else {
$button.enable(); $button.enable();
......
/* eslint-disable func-names, no-var, no-else-return, consistent-return, prefer-template, one-var, no-return-assign, no-unused-expressions, no-sequences */ /* eslint-disable func-names, no-var, no-else-return, consistent-return, one-var, no-return-assign, no-unused-expressions, no-sequences */
import $ from 'jquery'; import $ from 'jquery';
...@@ -49,13 +49,13 @@ export default class ImageFile { ...@@ -49,13 +49,13 @@ export default class ImageFile {
activateViewMode(viewMode) { activateViewMode(viewMode) {
$('.view-modes-menu li', this.file) $('.view-modes-menu li', this.file)
.removeClass('active') .removeClass('active')
.filter('.' + viewMode) .filter(`.${viewMode}`)
.addClass('active'); .addClass('active');
return $('.view:visible:not(.' + viewMode + ')', this.file).fadeOut( return $(`.view:visible:not(.${viewMode})`, this.file).fadeOut(
200, 200,
(function(_this) { (function(_this) {
return function() { return function() {
$('.view.' + viewMode, _this.file).fadeIn(200); $(`.view.${viewMode}`, _this.file).fadeIn(200);
return _this.initView(viewMode); return _this.initView(viewMode);
}; };
})(this), })(this),
...@@ -139,8 +139,8 @@ export default class ImageFile { ...@@ -139,8 +139,8 @@ export default class ImageFile {
} }
}); });
return _this.requestImageInfo($('img', wrap), (width, height) => { return _this.requestImageInfo($('img', wrap), (width, height) => {
$('.image-info .meta-width', wrap).text(width + 'px'); $('.image-info .meta-width', wrap).text(`${width}px`);
$('.image-info .meta-height', wrap).text(height + 'px'); $('.image-info .meta-height', wrap).text(`${height}px`);
return $('.image-info', wrap).removeClass('hide'); return $('.image-info', wrap).removeClass('hide');
}); });
}; };
......
/* eslint-disable func-names, no-underscore-dangle, no-var, one-var, vars-on-top, no-shadow, no-cond-assign, no-return-assign, no-else-return, camelcase, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, prefer-template, no-param-reassign, no-loop-func */ /* eslint-disable func-names, no-underscore-dangle, no-var, one-var, vars-on-top, no-shadow, no-cond-assign, no-return-assign, no-else-return, camelcase, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, no-param-reassign, no-loop-func */
import $ from 'jquery'; import $ from 'jquery';
import _ from 'underscore'; import _ from 'underscore';
...@@ -272,7 +272,7 @@ GitLabDropdown = (function() { ...@@ -272,7 +272,7 @@ GitLabDropdown = (function() {
NON_SELECTABLE_CLASSES = '.divider, .separator, .dropdown-header, .dropdown-menu-empty-item'; NON_SELECTABLE_CLASSES = '.divider, .separator, .dropdown-header, .dropdown-menu-empty-item';
SELECTABLE_CLASSES = '.dropdown-content li:not(' + NON_SELECTABLE_CLASSES + ', .option-hidden)'; SELECTABLE_CLASSES = `.dropdown-content li:not(${NON_SELECTABLE_CLASSES}, .option-hidden)`;
CURSOR_SELECT_SCROLL_PADDING = 5; CURSOR_SELECT_SCROLL_PADDING = 5;
...@@ -359,9 +359,9 @@ GitLabDropdown = (function() { ...@@ -359,9 +359,9 @@ GitLabDropdown = (function() {
instance: this, instance: this,
elements: (function(_this) { elements: (function(_this) {
return function() { return function() {
selector = '.dropdown-content li:not(' + NON_SELECTABLE_CLASSES + ')'; selector = `.dropdown-content li:not(${NON_SELECTABLE_CLASSES})`;
if (_this.dropdown.find('.dropdown-toggle-page').length) { if (_this.dropdown.find('.dropdown-toggle-page').length) {
selector = '.dropdown-page-one ' + selector; selector = `.dropdown-page-one ${selector}`;
} }
return $(selector, this.instance.dropdown); return $(selector, this.instance.dropdown);
}; };
...@@ -377,7 +377,7 @@ GitLabDropdown = (function() { ...@@ -377,7 +377,7 @@ GitLabDropdown = (function() {
if (_this.filterInput.val() !== '') { if (_this.filterInput.val() !== '') {
selector = SELECTABLE_CLASSES; selector = SELECTABLE_CLASSES;
if (_this.dropdown.find('.dropdown-toggle-page').length) { if (_this.dropdown.find('.dropdown-toggle-page').length) {
selector = '.dropdown-page-one ' + selector; selector = `.dropdown-page-one ${selector}`;
} }
if ($(_this.el).is('input')) { if ($(_this.el).is('input')) {
currentIndex = -1; currentIndex = -1;
...@@ -693,7 +693,7 @@ GitLabDropdown = (function() { ...@@ -693,7 +693,7 @@ GitLabDropdown = (function() {
.split('') .split('')
.map((character, i) => { .map((character, i) => {
if (indexOf.call(occurrences, i) !== -1) { if (indexOf.call(occurrences, i) !== -1) {
return '<b>' + character + '</b>'; return `<b>${character}</b>`;
} else { } else {
return character; return character;
} }
...@@ -738,9 +738,7 @@ GitLabDropdown = (function() { ...@@ -738,9 +738,7 @@ GitLabDropdown = (function() {
} else if (value != null) { } else if (value != null) {
field = this.dropdown field = this.dropdown
.parent() .parent()
.find( .find(`input[name='${fieldName}'][value='${value.toString().replace(/'/g, "\\'")}']`);
"input[name='" + fieldName + "'][value='" + value.toString().replace(/'/g, "\\'") + "']",
);
} }
if (this.options.isSelectable && !this.options.isSelectable(selectedObject, el)) { if (this.options.isSelectable && !this.options.isSelectable(selectedObject, el)) {
...@@ -766,11 +764,11 @@ GitLabDropdown = (function() { ...@@ -766,11 +764,11 @@ GitLabDropdown = (function() {
} else { } else {
isMarking = true; isMarking = true;
if (!this.options.multiSelect || el.hasClass('dropdown-clear-active')) { if (!this.options.multiSelect || el.hasClass('dropdown-clear-active')) {
this.dropdown.find('.' + ACTIVE_CLASS).removeClass(ACTIVE_CLASS); this.dropdown.find(`.${ACTIVE_CLASS}`).removeClass(ACTIVE_CLASS);
if (!isInput) { if (!isInput) {
this.dropdown this.dropdown
.parent() .parent()
.find("input[name='" + fieldName + "']") .find(`input[name='${fieldName}']`)
.remove(); .remove();
} }
} }
...@@ -809,7 +807,7 @@ GitLabDropdown = (function() { ...@@ -809,7 +807,7 @@ GitLabDropdown = (function() {
var $input; var $input;
// Create hidden input for form // Create hidden input for form
if (single) { if (single) {
$('input[name="' + fieldName + '"]').remove(); $(`input[name="${fieldName}"]`).remove();
} }
$input = $('<input>') $input = $('<input>')
...@@ -837,12 +835,12 @@ GitLabDropdown = (function() { ...@@ -837,12 +835,12 @@ GitLabDropdown = (function() {
var $el, selector; var $el, selector;
// If we pass an option index // If we pass an option index
if (typeof index !== 'undefined') { if (typeof index !== 'undefined') {
selector = SELECTABLE_CLASSES + ':eq(' + index + ') a'; selector = `${SELECTABLE_CLASSES}:eq(${index}) a`;
} else { } else {
selector = '.dropdown-content .is-focused'; selector = '.dropdown-content .is-focused';
} }
if (this.dropdown.find('.dropdown-toggle-page').length) { if (this.dropdown.find('.dropdown-toggle-page').length) {
selector = '.dropdown-page-one ' + selector; selector = `.dropdown-page-one ${selector}`;
} }
// simulate a click on the first link // simulate a click on the first link
$el = $(selector, this.dropdown); $el = $(selector, this.dropdown);
...@@ -861,7 +859,7 @@ GitLabDropdown = (function() { ...@@ -861,7 +859,7 @@ GitLabDropdown = (function() {
ARROW_KEY_CODES = [38, 40]; ARROW_KEY_CODES = [38, 40];
selector = SELECTABLE_CLASSES; selector = SELECTABLE_CLASSES;
if (this.dropdown.find('.dropdown-toggle-page').length) { if (this.dropdown.find('.dropdown-toggle-page').length) {
selector = '.dropdown-page-one ' + selector; selector = `.dropdown-page-one ${selector}`;
} }
return $('body').on( return $('body').on(
'keydown', 'keydown',
......
/* eslint-disable no-useless-return, func-names, no-var, no-underscore-dangle, one-var, prefer-template, no-new, consistent-return, no-shadow, no-param-reassign, vars-on-top, no-lonely-if, no-else-return, dot-notation, no-empty */ /* eslint-disable no-useless-return, func-names, no-var, no-underscore-dangle, one-var, no-new, consistent-return, no-shadow, no-param-reassign, vars-on-top, no-lonely-if, no-else-return, dot-notation, no-empty */
/* global Issuable */ /* global Issuable */
/* global ListLabel */ /* global ListLabel */
...@@ -70,7 +70,7 @@ export default class LabelsSelect { ...@@ -70,7 +70,7 @@ export default class LabelsSelect {
$loading = $block.find('.block-loading').fadeOut(); $loading = $block.find('.block-loading').fadeOut();
fieldName = $dropdown.data('fieldName'); fieldName = $dropdown.data('fieldName');
initialSelected = $selectbox initialSelected = $selectbox
.find('input[name="' + $dropdown.data('fieldName') + '"]') .find(`input[name="${$dropdown.data('fieldName')}"]`)
.map(function() { .map(function() {
return this.value; return this.value;
}) })
...@@ -92,7 +92,7 @@ export default class LabelsSelect { ...@@ -92,7 +92,7 @@ export default class LabelsSelect {
var data, selected; var data, selected;
selected = $dropdown selected = $dropdown
.closest('.selectbox') .closest('.selectbox')
.find("input[name='" + fieldName + "']") .find(`input[name='${fieldName}']`)
.map(function() { .map(function() {
return this.value; return this.value;
}) })
...@@ -267,11 +267,7 @@ export default class LabelsSelect { ...@@ -267,11 +267,7 @@ export default class LabelsSelect {
if ( if (
$form.find( $form.find(
"input[type='hidden'][name='" + `input[type='hidden'][name='${this.fieldName}'][value='${dropdownValue}']`,
this.fieldName +
"'][value='" +
dropdownValue +
"']",
).length ).length
) { ) {
selectedClass.push('is-active'); selectedClass.push('is-active');
...@@ -284,8 +280,7 @@ export default class LabelsSelect { ...@@ -284,8 +280,7 @@ export default class LabelsSelect {
} }
if (label.color) { if (label.color) {
colorEl = colorEl = `<span class='dropdown-label-box' style='background: ${label.color}'></span>`;
"<span class='dropdown-label-box' style='background: " + label.color + "'></span>";
} else { } else {
colorEl = ''; colorEl = '';
} }
......
/* eslint-disable func-names, no-var, no-param-reassign, one-var, operator-assignment, no-else-return, prefer-template, consistent-return */ /* eslint-disable func-names, no-var, no-param-reassign, one-var, operator-assignment, no-else-return, consistent-return */
import $ from 'jquery'; import $ from 'jquery';
import { insertText } from '~/lib/utils/common_utils'; import { insertText } from '~/lib/utils/common_utils';
...@@ -237,7 +237,7 @@ export function insertMarkdownText({ ...@@ -237,7 +237,7 @@ export function insertMarkdownText({
} }
if (removedFirstNewLine) { if (removedFirstNewLine) {
textToInsert = '\n' + textToInsert; textToInsert = `\n${textToInsert}`;
} }
if (removedLastNewLine) { if (removedLastNewLine) {
......
/* eslint-disable func-names, no-var, no-underscore-dangle, no-param-reassign, prefer-template, consistent-return, one-var, no-else-return */ /* eslint-disable func-names, no-var, no-underscore-dangle, no-param-reassign, consistent-return, one-var, no-else-return */
import $ from 'jquery'; import $ from 'jquery';
...@@ -106,7 +106,7 @@ LineHighlighter.prototype.clickHandler = function(event) { ...@@ -106,7 +106,7 @@ LineHighlighter.prototype.clickHandler = function(event) {
}; };
LineHighlighter.prototype.clearHighlight = function() { LineHighlighter.prototype.clearHighlight = function() {
return $('.' + this.highlightLineClass).removeClass(this.highlightLineClass); return $(`.${this.highlightLineClass}`).removeClass(this.highlightLineClass);
}; };
// Convert a URL hash String into line numbers // Convert a URL hash String into line numbers
...@@ -137,7 +137,7 @@ LineHighlighter.prototype.hashToRange = function(hash) { ...@@ -137,7 +137,7 @@ LineHighlighter.prototype.hashToRange = function(hash) {
// //
// lineNumber - Line number to highlight // lineNumber - Line number to highlight
LineHighlighter.prototype.highlightLine = function(lineNumber) { LineHighlighter.prototype.highlightLine = function(lineNumber) {
return $('#LC' + lineNumber).addClass(this.highlightLineClass); return $(`#LC${lineNumber}`).addClass(this.highlightLineClass);
}; };
// Highlight all lines within a range // Highlight all lines within a range
...@@ -162,9 +162,9 @@ LineHighlighter.prototype.highlightRange = function(range) { ...@@ -162,9 +162,9 @@ LineHighlighter.prototype.highlightRange = function(range) {
LineHighlighter.prototype.setHash = function(firstLineNumber, lastLineNumber) { LineHighlighter.prototype.setHash = function(firstLineNumber, lastLineNumber) {
var hash; var hash;
if (lastLineNumber) { if (lastLineNumber) {
hash = '#L' + firstLineNumber + '-' + lastLineNumber; hash = `#L${firstLineNumber}-${lastLineNumber}`;
} else { } else {
hash = '#L' + firstLineNumber; hash = `#L${firstLineNumber}`;
} }
this._hash = hash; this._hash = hash;
return this.__setLocationHash__(hash); return this.__setLocationHash__(hash);
......
/* eslint-disable no-else-return, prefer-template */ /* eslint-disable no-else-return */
import $ from 'jquery'; import $ from 'jquery';
import '~/gl_dropdown'; import '~/gl_dropdown';
...@@ -24,7 +24,7 @@ export default class NamespaceSelect { ...@@ -24,7 +24,7 @@ export default class NamespaceSelect {
if (selected.id == null) { if (selected.id == null) {
return selected.text; return selected.text;
} else { } else {
return selected.kind + ': ' + selected.full_path; return `${selected.kind}: ${selected.full_path}`;
} }
}, },
data(term, dataCallback) { data(term, dataCallback) {
...@@ -44,7 +44,7 @@ export default class NamespaceSelect { ...@@ -44,7 +44,7 @@ export default class NamespaceSelect {
if (namespace.id == null) { if (namespace.id == null) {
return namespace.text; return namespace.text;
} else { } else {
return namespace.kind + ': ' + namespace.full_path; return `${namespace.kind}: ${namespace.full_path}`;
} }
}, },
renderRow: this.renderRow, renderRow: this.renderRow,
......
/* eslint-disable func-names, no-var, one-var, no-loop-func, consistent-return, prefer-template, camelcase */ /* eslint-disable func-names, no-var, one-var, no-loop-func, consistent-return, camelcase */
import $ from 'jquery'; import $ from 'jquery';
import { __ } from '../locale'; import { __ } from '../locale';
...@@ -223,7 +223,7 @@ export default (function() { ...@@ -223,7 +223,7 @@ export default (function() {
shortrefs = commit.refs; shortrefs = commit.refs;
// Truncate if longer than 15 chars // Truncate if longer than 15 chars
if (shortrefs.length > 17) { if (shortrefs.length > 17) {
shortrefs = shortrefs.substr(0, 15) + ''; shortrefs = `${shortrefs.substr(0, 15)}…`;
} }
text = r.text(x + 4, y, shortrefs).attr({ text = r.text(x + 4, y, shortrefs).attr({
'text-anchor': 'start', 'text-anchor': 'start',
......
/* eslint-disable func-names, no-var, one-var, consistent-return, no-return-assign, prefer-template, no-shadow, no-else-return, @gitlab/i18n/no-non-i18n-strings */ /* eslint-disable func-names, no-var, one-var, consistent-return, no-return-assign, no-shadow, no-else-return, @gitlab/i18n/no-non-i18n-strings */
import $ from 'jquery'; import $ from 'jquery';
import RefSelectDropdown from './ref_select_dropdown'; import RefSelectDropdown from './ref_select_dropdown';
...@@ -70,10 +70,10 @@ export default class NewBranchForm { ...@@ -70,10 +70,10 @@ export default class NewBranchForm {
case !/\/{2,}/g.test(value): case !/\/{2,}/g.test(value):
return 'consecutive slashes'; return 'consecutive slashes';
default: default:
return "'" + value + "'"; return `'${value}'`;
} }
}); });
return restriction.prefix + ' ' + formatted.join(restriction.conjunction); return `${restriction.prefix} ${formatted.join(restriction.conjunction)}`;
}; };
validator = (function(_this) { validator = (function(_this) {
return function(errors, restriction) { return function(errors, restriction) {
......
/* eslint-disable no-restricted-properties, func-names, no-var, camelcase, /* eslint-disable no-restricted-properties, func-names, no-var, camelcase,
no-unused-expressions, one-var, default-case, no-unused-expressions, one-var, default-case,
prefer-template, consistent-return, no-alert, no-return-assign, consistent-return, no-alert, no-return-assign,
no-param-reassign, no-else-return, vars-on-top, no-param-reassign, no-else-return, vars-on-top,
no-shadow, no-useless-escape, class-methods-use-this */ no-shadow, no-useless-escape, class-methods-use-this */
...@@ -490,7 +490,7 @@ export default class Notes { ...@@ -490,7 +490,7 @@ export default class Notes {
diffAvatarContainer = row diffAvatarContainer = row
.prevAll('.line_holder') .prevAll('.line_holder')
.first() .first()
.find('.js-avatar-container.' + lineType + '_line'); .find(`.js-avatar-container.${lineType}_line`);
// is this the first note of discussion? // is this the first note of discussion?
discussionContainer = $(`.notes[data-discussion-id="${noteEntity.discussion_id}"]`); discussionContainer = $(`.notes[data-discussion-id="${noteEntity.discussion_id}"]`);
if (!discussionContainer.length) { if (!discussionContainer.length) {
...@@ -506,16 +506,14 @@ export default class Notes { ...@@ -506,16 +506,14 @@ export default class Notes {
} else { } else {
// Merge new discussion HTML in // Merge new discussion HTML in
var $notes = $discussion.find(`.notes[data-discussion-id="${noteEntity.discussion_id}"]`); var $notes = $discussion.find(`.notes[data-discussion-id="${noteEntity.discussion_id}"]`);
var contentContainerClass = var contentContainerClass = $notes
'.' + .closest('.notes-content')
$notes .attr('class')
.closest('.notes-content') .split(' ')
.attr('class') .join('.');
.split(' ')
.join('.');
row row
.find(contentContainerClass + ' .content') .find(`.${contentContainerClass} .content`)
.append($notes.closest('.content').children()); .append($notes.closest('.content').children());
} }
} else { } else {
...@@ -722,7 +720,7 @@ export default class Notes { ...@@ -722,7 +720,7 @@ export default class Notes {
this.revertNoteEditForm($targetNote); this.revertNoteEditForm($targetNote);
$noteEntityEl.renderGFM(); $noteEntityEl.renderGFM();
// Find the note's `li` element by ID and replace it with the updated HTML // Find the note's `li` element by ID and replace it with the updated HTML
$note_li = $('.note-row-' + noteEntity.id); $note_li = $(`.note-row-${noteEntity.id}`);
$note_li.replaceWith($noteEntityEl); $note_li.replaceWith($noteEntityEl);
this.setupNewNote($noteEntityEl); this.setupNewNote($noteEntityEl);
......
/* eslint-disable func-names, no-var, one-var, camelcase, no-param-reassign, prefer-template, no-return-assign */ /* eslint-disable func-names, no-var, one-var, camelcase, no-param-reassign, no-return-assign */
import $ from 'jquery'; import $ from 'jquery';
import _ from 'underscore'; import _ from 'underscore';
...@@ -66,8 +66,8 @@ export default (function() { ...@@ -66,8 +66,8 @@ export default (function() {
class: 'person', class: 'person',
style: 'display: block;', style: 'display: block;',
}); });
author_name = $('<h4>' + author.author_name + '</h4>'); author_name = $(`<h4>${author.author_name}</h4>`);
author_email = $('<p class="graph-author-email">' + author.author_email + '</p>'); author_email = $(`<p class="graph-author-email">${author.author_email}</p>`);
author_commit_info_span = $('<span/>', { author_commit_info_span = $('<span/>', {
class: 'commits', class: 'commits',
}); });
......
/* eslint-disable func-names, no-restricted-syntax, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, no-return-assign, prefer-template, no-else-return, no-shadow */ /* eslint-disable func-names, no-restricted-syntax, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, no-return-assign, no-else-return, no-shadow */
import $ from 'jquery'; import $ from 'jquery';
import _ from 'underscore'; import _ from 'underscore';
...@@ -118,14 +118,11 @@ export const ContributorsGraph = (function() { ...@@ -118,14 +118,11 @@ export const ContributorsGraph = (function() {
}; };
ContributorsGraph.prototype.draw_x_axis = function() { ContributorsGraph.prototype.draw_x_axis = function() {
return ( return this.svg
this.svg .append('g')
.append('g') .attr('class', 'x axis')
.attr('class', 'x axis') .attr('transform', `translate(0, ${this.height})`)
/* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ .call(this.x_axis);
.attr('transform', 'translate(0, ' + this.height + ')')
.call(this.x_axis)
);
}; };
ContributorsGraph.prototype.draw_y_axis = function() { ContributorsGraph.prototype.draw_y_axis = function() {
...@@ -200,8 +197,7 @@ export const ContributorsMasterGraph = (function(superClass) { ...@@ -200,8 +197,7 @@ export const ContributorsMasterGraph = (function(superClass) {
.attr('height', this.height + this.MARGIN.top + this.MARGIN.bottom) .attr('height', this.height + this.MARGIN.top + this.MARGIN.bottom)
.attr('class', 'tint-box') .attr('class', 'tint-box')
.append('g') .append('g')
/* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ .attr('transform', `translate(${this.MARGIN.left},${this.MARGIN.top})`);
.attr('transform', 'translate(' + this.MARGIN.left + ',' + this.MARGIN.top + ')');
return this.svg; return this.svg;
}; };
...@@ -348,8 +344,7 @@ export const ContributorsAuthorGraph = (function(superClass) { ...@@ -348,8 +344,7 @@ export const ContributorsAuthorGraph = (function(superClass) {
.attr('height', this.height + this.MARGIN.top + this.MARGIN.bottom) .attr('height', this.height + this.MARGIN.top + this.MARGIN.bottom)
.attr('class', 'spark') .attr('class', 'spark')
.append('g') .append('g')
/* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ .attr('transform', `translate(${this.MARGIN.left},${this.MARGIN.top})`);
.attr('transform', 'translate(' + this.MARGIN.left + ',' + this.MARGIN.top + ')');
return this.svg; return this.svg;
}; };
......
/* eslint-disable func-names, no-var, prefer-template */ /* eslint-disable func-names, no-var */
import $ from 'jquery'; import $ from 'jquery';
import BranchGraph from '../../../network/branch_graph'; import BranchGraph from '../../../network/branch_graph';
...@@ -14,7 +14,7 @@ export default (function() { ...@@ -14,7 +14,7 @@ export default (function() {
this.branch_graph = new BranchGraph($('.network-graph'), opts); this.branch_graph = new BranchGraph($('.network-graph'), opts);
vph = $(window).height() - 250; vph = $(window).height() - 250;
$('.network-graph').css({ $('.network-graph').css({
height: vph + 'px', height: `${vph}px`,
}); });
} }
......
/* eslint-disable func-names, no-var, consistent-return, one-var, no-cond-assign, prefer-template, no-return-assign */ /* eslint-disable func-names, no-var, consistent-return, one-var, no-cond-assign, no-return-assign */
import $ from 'jquery'; import $ from 'jquery';
import fuzzaldrinPlus from 'fuzzaldrin-plus'; import fuzzaldrinPlus from 'fuzzaldrin-plus';
...@@ -112,7 +112,7 @@ export default class ProjectFindFile { ...@@ -112,7 +112,7 @@ export default class ProjectFindFile {
if (searchText) { if (searchText) {
matches = fuzzaldrinPlus.match(filePath, searchText); matches = fuzzaldrinPlus.match(filePath, searchText);
} }
blobItemUrl = this.options.blobUrlTemplate + '/' + encodeURIComponent(filePath); blobItemUrl = `${this.options.blobUrlTemplate}/${encodeURIComponent(filePath)}`;
html = ProjectFindFile.makeHtml(filePath, matches, blobItemUrl); html = ProjectFindFile.makeHtml(filePath, matches, blobItemUrl);
results.push(this.element.find('.tree-table > tbody').append(html)); results.push(this.element.find('.tree-table > tbody').append(html));
} }
......
/* eslint-disable func-names, no-var, consistent-return, one-var, prefer-template, no-else-return, no-param-reassign */ /* eslint-disable func-names, no-var, consistent-return, one-var, no-else-return, no-param-reassign */
import $ from 'jquery'; import $ from 'jquery';
import _ from 'underscore'; import _ from 'underscore';
...@@ -247,7 +247,7 @@ Sidebar.prototype.isOpen = function() { ...@@ -247,7 +247,7 @@ Sidebar.prototype.isOpen = function() {
}; };
Sidebar.prototype.getBlock = function(name) { Sidebar.prototype.getBlock = function(name) {
return this.sidebar.find('.block.' + name); return this.sidebar.find(`.block.${name}`);
}; };
export default Sidebar; export default Sidebar;
/* eslint-disable no-return-assign, one-var, no-var, consistent-return, prefer-template, class-methods-use-this, no-lonely-if, vars-on-top */ /* eslint-disable no-return-assign, one-var, no-var, consistent-return, class-methods-use-this, no-lonely-if, vars-on-top */
import $ from 'jquery'; import $ from 'jquery';
import { escape, throttle } from 'underscore'; import { escape, throttle } from 'underscore';
...@@ -416,7 +416,7 @@ export class SearchAutocomplete { ...@@ -416,7 +416,7 @@ export class SearchAutocomplete {
inputs = Object.keys(this.originalState); inputs = Object.keys(this.originalState);
for (i = 0, len = inputs.length; i < len; i += 1) { for (i = 0, len = inputs.length; i < len; i += 1) {
input = inputs[i]; input = inputs[i];
this.getElement('#' + input).val(this.originalState[input]); this.getElement(`#${input}`).val(this.originalState[input]);
} }
} }
...@@ -426,7 +426,7 @@ export class SearchAutocomplete { ...@@ -426,7 +426,7 @@ export class SearchAutocomplete {
results = []; results = [];
for (i = 0, len = inputs.length; i < len; i += 1) { for (i = 0, len = inputs.length; i < len; i += 1) {
input = inputs[i]; input = inputs[i];
results.push(this.getElement('#' + input).val('')); results.push(this.getElement(`#${input}`).val(''));
} }
return results; return results;
} }
......
/* eslint-disable func-names, one-var, no-var, prefer-rest-params, vars-on-top, consistent-return, no-shadow, no-else-return, no-self-compare, prefer-template, no-unused-expressions, yoda, prefer-spread, camelcase, no-param-reassign */ /* eslint-disable func-names, one-var, no-var, prefer-rest-params, vars-on-top, consistent-return, no-shadow, no-else-return, no-self-compare, no-unused-expressions, yoda, prefer-spread, camelcase, no-param-reassign */
/* global Issuable */ /* global Issuable */
/* global emitSidebarEvent */ /* global emitSidebarEvent */
...@@ -428,8 +428,7 @@ function UsersSelect(currentUser, els, options = {}) { ...@@ -428,8 +428,7 @@ function UsersSelect(currentUser, els, options = {}) {
const isActive = $el.hasClass('is-active'); const isActive = $el.hasClass('is-active');
const previouslySelected = $dropdown const previouslySelected = $dropdown
.closest('.selectbox') .closest('.selectbox')
/* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ .find(`input[name='${$dropdown.data('fieldName')}'][value!=0]`);
.find("input[name='" + $dropdown.data('fieldName') + "'][value!=0]");
// Enables support for limiting the number of users selected // Enables support for limiting the number of users selected
// Automatically removes the first on the list if more users are selected // Automatically removes the first on the list if more users are selected
...@@ -448,7 +447,7 @@ function UsersSelect(currentUser, els, options = {}) { ...@@ -448,7 +447,7 @@ function UsersSelect(currentUser, els, options = {}) {
// Remove unassigned selection (if it was previously selected) // Remove unassigned selection (if it was previously selected)
const unassignedSelected = $dropdown const unassignedSelected = $dropdown
.closest('.selectbox') .closest('.selectbox')
.find("input[name='" + $dropdown.data('fieldName') + "'][value=0]"); .find(`input[name='${$dropdown.data('fieldName')}'][value=0]`);
if (unassignedSelected) { if (unassignedSelected) {
unassignedSelected.remove(); unassignedSelected.remove();
...@@ -502,7 +501,7 @@ function UsersSelect(currentUser, els, options = {}) { ...@@ -502,7 +501,7 @@ function UsersSelect(currentUser, els, options = {}) {
} else if (!$dropdown.hasClass('js-multiselect')) { } else if (!$dropdown.hasClass('js-multiselect')) {
selected = $dropdown selected = $dropdown
.closest('.selectbox') .closest('.selectbox')
.find("input[name='" + $dropdown.data('fieldName') + "']") .find(`input[name='${$dropdown.data('fieldName')}']`)
.val(); .val();
return assignTo(selected); return assignTo(selected);
} }
...@@ -544,7 +543,7 @@ function UsersSelect(currentUser, els, options = {}) { ...@@ -544,7 +543,7 @@ function UsersSelect(currentUser, els, options = {}) {
updateLabel: $dropdown.data('dropdownTitle'), updateLabel: $dropdown.data('dropdownTitle'),
renderRow(user) { renderRow(user) {
var avatar, img, username; var avatar, img, username;
username = user.username ? '@' + user.username : ''; username = user.username ? `@${user.username}` : '';
avatar = user.avatar_url ? user.avatar_url : gon.default_avatar_url; avatar = user.avatar_url ? user.avatar_url : gon.default_avatar_url;
let selected = false; let selected = false;
...@@ -555,7 +554,7 @@ function UsersSelect(currentUser, els, options = {}) { ...@@ -555,7 +554,7 @@ function UsersSelect(currentUser, els, options = {}) {
const { fieldName } = this; const { fieldName } = this;
const field = $dropdown const field = $dropdown
.closest('.selectbox') .closest('.selectbox')
.find("input[name='" + fieldName + "'][value='" + user.id + "']"); .find(`input[name='${fieldName}'][value='${user.id}']`);
if (field.length) { if (field.length) {
selected = true; selected = true;
...@@ -571,7 +570,7 @@ function UsersSelect(currentUser, els, options = {}) { ...@@ -571,7 +570,7 @@ function UsersSelect(currentUser, els, options = {}) {
)}</a></li>`; )}</a></li>`;
} else { } else {
// 0 margin, because it's now handled by a wrapper // 0 margin, because it's now handled by a wrapper
img = "<img src='" + avatar + "' class='avatar avatar-inline m-0' width='32' />"; img = `<img src='${avatar}' class='avatar avatar-inline m-0' width='32' />`;
} }
return _this.renderRow(options.issuableType, user, selected, username, img); return _this.renderRow(options.issuableType, user, selected, username, img);
...@@ -715,7 +714,7 @@ UsersSelect.prototype.formatResult = function(user) { ...@@ -715,7 +714,7 @@ UsersSelect.prototype.formatResult = function(user) {
${_.escape(user.name)} ${_.escape(user.name)}
</div> </div>
<div class='user-username dropdown-menu-user-username text-secondary'> <div class='user-username dropdown-menu-user-username text-secondary'>
${!user.invite ? '@' + _.escape(user.username) : ''} ${!user.invite ? `@${_.escape(user.username)}` : ''}
</div> </div>
</div> </div>
</div> </div>
......
---
title: Use GetBlobs RPC for uri type
merge_request: 16824
author:
type: performance
...@@ -20,16 +20,12 @@ module Banzai ...@@ -20,16 +20,12 @@ module Banzai
def call def call
return doc if context[:system_note] return doc if context[:system_note]
@uri_types = {}
clear_memoization(:linkable_files) clear_memoization(:linkable_files)
doc.search('a:not(.gfm)').each do |el| load_uri_types
process_link_attr el.attribute('href')
end
doc.css('img, video').each do |el| linkable_attributes.each do |attr|
process_link_attr el.attribute('src') process_link_attr(attr)
process_link_attr el.attribute('data-src')
end end
doc doc
...@@ -37,16 +33,81 @@ module Banzai ...@@ -37,16 +33,81 @@ module Banzai
protected protected
def load_uri_types
return unless linkable_files?
return {} unless repository
clear_memoization(:linkable_attributes)
@uri_types = request_path.present? ? get_uri_types([request_path]) : {}
paths = linkable_attributes.flat_map do |attr|
[get_uri(attr).to_s, relative_file_path(get_uri(attr))]
end
paths.reject!(&:blank?)
paths.uniq!
@uri_types.merge!(get_uri_types(paths))
end
def linkable_files? def linkable_files?
strong_memoize(:linkable_files) do strong_memoize(:linkable_files) do
context[:project_wiki].nil? && repository.try(:exists?) && !repository.empty? context[:project_wiki].nil? && repository.try(:exists?) && !repository.empty?
end end
end end
def process_link_attr(html_attr) def linkable_attributes
return if html_attr.blank? strong_memoize(:linkable_attributes) do
return if html_attr.value.start_with?('//') attrs = []
attrs += doc.search('a:not(.gfm)').map do |el|
el.attribute('href')
end
attrs += doc.search('img, video').flat_map do |el|
[el.attribute('src'), el.attribute('data-src')]
end
attrs.reject do |attr|
attr.blank? || attr.value.start_with?('//')
end
end
end
def get_uri_types(paths)
return {} if paths.empty?
uri_types = Hash[paths.collect { |name| [name, nil] }]
get_blob_types(paths).each do |name, type|
if type == :blob
blob = ::Blob.decorate(Gitlab::Git::Blob.new(name: name), project)
uri_types[name] = blob.image? || blob.video? ? :raw : :blob
else
uri_types[name] = type
end
end
uri_types
end
def get_blob_types(paths)
revision_paths = paths.collect do |path|
[current_commit.sha, path.chomp("/")]
end
Gitlab::GitalyClient::BlobService.new(repository).get_blob_types(revision_paths, 1)
end
def get_uri(html_attr)
uri = URI(html_attr.value)
uri if uri.relative? && uri.path.present?
rescue URI::Error, Addressable::URI::InvalidURIError
end
def process_link_attr(html_attr)
if html_attr.value.start_with?('/uploads/') if html_attr.value.start_with?('/uploads/')
process_link_to_upload_attr(html_attr) process_link_to_upload_attr(html_attr)
elsif linkable_files? && repo_visible_to_user? elsif linkable_files? && repo_visible_to_user?
...@@ -81,6 +142,7 @@ module Banzai ...@@ -81,6 +142,7 @@ module Banzai
def process_link_to_repository_attr(html_attr) def process_link_to_repository_attr(html_attr)
uri = URI(html_attr.value) uri = URI(html_attr.value)
if uri.relative? && uri.path.present? if uri.relative? && uri.path.present?
html_attr.value = rebuild_relative_uri(uri).to_s html_attr.value = rebuild_relative_uri(uri).to_s
end end
...@@ -89,7 +151,7 @@ module Banzai ...@@ -89,7 +151,7 @@ module Banzai
end end
def rebuild_relative_uri(uri) def rebuild_relative_uri(uri)
file_path = relative_file_path(uri) file_path = nested_file_path_if_exists(uri)
uri.path = [ uri.path = [
relative_url_root, relative_url_root,
...@@ -102,13 +164,29 @@ module Banzai ...@@ -102,13 +164,29 @@ module Banzai
uri uri
end end
def relative_file_path(uri) def nested_file_path_if_exists(uri)
path = Addressable::URI.unescape(uri.path).delete("\0") path = cleaned_file_path(uri)
request_path = Addressable::URI.unescape(context[:requested_path]) nested_path = relative_file_path(uri)
nested_path = build_relative_path(path, request_path)
file_exists?(nested_path) ? nested_path : path file_exists?(nested_path) ? nested_path : path
end end
def cleaned_file_path(uri)
Addressable::URI.unescape(uri.path).delete("\0").chomp("/")
end
def relative_file_path(uri)
return if uri.nil?
build_relative_path(cleaned_file_path(uri), request_path)
end
def request_path
return unless context[:requested_path]
Addressable::URI.unescape(context[:requested_path]).chomp("/")
end
# Convert a relative path into its correct location based on the currently # Convert a relative path into its correct location based on the currently
# requested path # requested path
# #
...@@ -136,6 +214,7 @@ module Banzai ...@@ -136,6 +214,7 @@ module Banzai
return path[1..-1] if path.start_with?('/') return path[1..-1] if path.start_with?('/')
parts = request_path.split('/') parts = request_path.split('/')
parts.pop if uri_type(request_path) != :tree parts.pop if uri_type(request_path) != :tree
path.sub!(%r{\A\./}, '') path.sub!(%r{\A\./}, '')
...@@ -149,14 +228,11 @@ module Banzai ...@@ -149,14 +228,11 @@ module Banzai
end end
def file_exists?(path) def file_exists?(path)
path.present? && !!uri_type(path) path.present? && uri_type(path).present?
end end
def uri_type(path) def uri_type(path)
# https://gitlab.com/gitlab-org/gitlab-foss/issues/58657 @uri_types[path] == :unknown ? "" : @uri_types[path]
Gitlab::GitalyClient.allow_n_plus_1_calls do
@uri_types[path] ||= current_commit.uri_type(path)
end
end end
def current_commit def current_commit
......
...@@ -76,6 +76,30 @@ module Gitlab ...@@ -76,6 +76,30 @@ module Gitlab
GitalyClient::BlobsStitcher.new(response) GitalyClient::BlobsStitcher.new(response)
end end
def get_blob_types(revision_paths, limit = -1)
return {} if revision_paths.empty?
request_revision_paths = revision_paths.map do |rev, path|
Gitaly::GetBlobsRequest::RevisionPath.new(revision: rev, path: encode_binary(path))
end
request = Gitaly::GetBlobsRequest.new(
repository: @gitaly_repo,
revision_paths: request_revision_paths,
limit: limit
)
response = GitalyClient.call(
@gitaly_repo.storage_name,
:blob_service,
:get_blobs,
request,
timeout: GitalyClient.fast_timeout
)
map_blob_types(response)
end
def get_new_lfs_pointers(revision, limit, not_in, dynamic_timeout = nil) def get_new_lfs_pointers(revision, limit, not_in, dynamic_timeout = nil)
request = Gitaly::GetNewLFSPointersRequest.new( request = Gitaly::GetNewLFSPointersRequest.new(
repository: @gitaly_repo, repository: @gitaly_repo,
...@@ -132,6 +156,16 @@ module Gitlab ...@@ -132,6 +156,16 @@ module Gitlab
end end
end end
end end
def map_blob_types(response)
types = {}
response.each do |msg|
types[msg.path.dup.force_encoding('utf-8')] = msg.type.downcase
end
types
end
end end
end end
end end
...@@ -234,6 +234,12 @@ describe 'Issue Boards', :js do ...@@ -234,6 +234,12 @@ describe 'Issue Boards', :js do
expect(find('.board:nth-child(2)')).to have_content(development.title) expect(find('.board:nth-child(2)')).to have_content(development.title)
expect(find('.board:nth-child(2)')).to have_content(planning.title) expect(find('.board:nth-child(2)')).to have_content(planning.title)
# Make sure list positions are preserved after a reload
visit project_board_path(project, board)
expect(find('.board:nth-child(2)')).to have_content(development.title)
expect(find('.board:nth-child(2)')).to have_content(planning.title)
end end
it 'dragging does not duplicate list' do it 'dragging does not duplicate list' do
......
/* eslint-disable no-var, prefer-template, no-else-return, dot-notation, no-return-assign, no-new, no-underscore-dangle */ /* eslint-disable no-var, no-else-return, dot-notation, no-return-assign, no-new, no-underscore-dangle */
import $ from 'jquery'; import $ from 'jquery';
import LineHighlighter from '~/line_highlighter'; import LineHighlighter from '~/line_highlighter';
...@@ -8,10 +8,10 @@ describe('LineHighlighter', function() { ...@@ -8,10 +8,10 @@ describe('LineHighlighter', function() {
preloadFixtures('static/line_highlighter.html'); preloadFixtures('static/line_highlighter.html');
clickLine = function(number, eventData = {}) { clickLine = function(number, eventData = {}) {
if ($.isEmptyObject(eventData)) { if ($.isEmptyObject(eventData)) {
return $('#L' + number).click(); return $(`#L${number}`).click();
} else { } else {
const e = $.Event('click', eventData); const e = $.Event('click', eventData);
return $('#L' + number).trigger(e); return $(`#L${number}`).trigger(e);
} }
}; };
beforeEach(function() { beforeEach(function() {
...@@ -42,9 +42,9 @@ describe('LineHighlighter', function() { ...@@ -42,9 +42,9 @@ describe('LineHighlighter', function() {
var line; var line;
new LineHighlighter({ hash: '#L5-25' }); new LineHighlighter({ hash: '#L5-25' });
expect($('.' + this.css).length).toBe(21); expect($(`.${this.css}`).length).toBe(21);
for (line = 5; line <= 25; line += 1) { for (line = 5; line <= 25; line += 1) {
expect($('#LC' + line)).toHaveClass(this.css); expect($(`#LC${line}`)).toHaveClass(this.css);
} }
}); });
...@@ -130,7 +130,7 @@ describe('LineHighlighter', function() { ...@@ -130,7 +130,7 @@ describe('LineHighlighter', function() {
}); });
expect($('#LC13')).toHaveClass(this.css); expect($('#LC13')).toHaveClass(this.css);
expect($('.' + this.css).length).toBe(1); expect($(`.${this.css}`).length).toBe(1);
}); });
it('sets the hash', function() { it('sets the hash', function() {
...@@ -152,9 +152,9 @@ describe('LineHighlighter', function() { ...@@ -152,9 +152,9 @@ describe('LineHighlighter', function() {
shiftKey: true, shiftKey: true,
}); });
expect($('.' + this.css).length).toBe(6); expect($(`.${this.css}`).length).toBe(6);
for (line = 15; line <= 20; line += 1) { for (line = 15; line <= 20; line += 1) {
expect($('#LC' + line)).toHaveClass(this.css); expect($(`#LC${line}`)).toHaveClass(this.css);
} }
}); });
...@@ -165,9 +165,9 @@ describe('LineHighlighter', function() { ...@@ -165,9 +165,9 @@ describe('LineHighlighter', function() {
shiftKey: true, shiftKey: true,
}); });
expect($('.' + this.css).length).toBe(6); expect($(`.${this.css}`).length).toBe(6);
for (line = 5; line <= 10; line += 1) { for (line = 5; line <= 10; line += 1) {
expect($('#LC' + line)).toHaveClass(this.css); expect($(`#LC${line}`)).toHaveClass(this.css);
} }
}); });
}); });
...@@ -188,9 +188,9 @@ describe('LineHighlighter', function() { ...@@ -188,9 +188,9 @@ describe('LineHighlighter', function() {
shiftKey: true, shiftKey: true,
}); });
expect($('.' + this.css).length).toBe(6); expect($(`.${this.css}`).length).toBe(6);
for (line = 5; line <= 10; line += 1) { for (line = 5; line <= 10; line += 1) {
expect($('#LC' + line)).toHaveClass(this.css); expect($(`#LC${line}`)).toHaveClass(this.css);
} }
}); });
...@@ -200,9 +200,9 @@ describe('LineHighlighter', function() { ...@@ -200,9 +200,9 @@ describe('LineHighlighter', function() {
shiftKey: true, shiftKey: true,
}); });
expect($('.' + this.css).length).toBe(6); expect($(`.${this.css}`).length).toBe(6);
for (line = 10; line <= 15; line += 1) { for (line = 10; line <= 15; line += 1) {
expect($('#LC' + line)).toHaveClass(this.css); expect($(`#LC${line}`)).toHaveClass(this.css);
} }
}); });
}); });
......
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
require 'spec_helper' require 'spec_helper'
describe Banzai::Filter::RelativeLinkFilter do describe Banzai::Filter::RelativeLinkFilter do
include GitHelpers
include RepoHelpers
def filter(doc, contexts = {}) def filter(doc, contexts = {})
contexts.reverse_merge!({ contexts.reverse_merge!({
commit: commit, commit: commit,
...@@ -34,6 +37,12 @@ describe Banzai::Filter::RelativeLinkFilter do ...@@ -34,6 +37,12 @@ describe Banzai::Filter::RelativeLinkFilter do
%(<div>#{element}</div>) %(<div>#{element}</div>)
end end
def allow_gitaly_n_plus_1
Gitlab::GitalyClient.allow_n_plus_1_calls do
yield
end
end
let(:project) { create(:project, :repository, :public) } let(:project) { create(:project, :repository, :public) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:group) { nil } let(:group) { nil }
...@@ -44,6 +53,19 @@ describe Banzai::Filter::RelativeLinkFilter do ...@@ -44,6 +53,19 @@ describe Banzai::Filter::RelativeLinkFilter do
let(:requested_path) { '/' } let(:requested_path) { '/' }
let(:only_path) { true } let(:only_path) { true }
it 'does not trigger a gitaly n+1', :request_store do
raw_doc = ""
allow_gitaly_n_plus_1 do
30.times do |i|
create_file_in_repo(project, ref, ref, "new_file_#{i}", "x" )
raw_doc += link("new_file_#{i}")
end
end
expect { filter(raw_doc) }.to change { Gitlab::GitalyClient.get_request_count }.by(2)
end
shared_examples :preserve_unchanged do shared_examples :preserve_unchanged do
it 'does not modify any relative URL in anchor' do it 'does not modify any relative URL in anchor' do
doc = filter(link('README.md')) doc = filter(link('README.md'))
...@@ -244,7 +266,8 @@ describe Banzai::Filter::RelativeLinkFilter do ...@@ -244,7 +266,8 @@ describe Banzai::Filter::RelativeLinkFilter do
end end
context 'when ref name contains special chars' do context 'when ref name contains special chars' do
let(:ref) {'mark#\'@],+;-._/#@!$&()+down'} let(:ref) { 'mark#\'@],+;-._/#@!$&()+down' }
let(:path) { 'files/images/logo-black.png' }
it 'correctly escapes the ref' do it 'correctly escapes the ref' do
# Addressable won't escape the '#', so we do this manually # Addressable won't escape the '#', so we do this manually
...@@ -252,8 +275,9 @@ describe Banzai::Filter::RelativeLinkFilter do ...@@ -252,8 +275,9 @@ describe Banzai::Filter::RelativeLinkFilter do
# Stub this method so the branch doesn't actually need to be in the repo # Stub this method so the branch doesn't actually need to be in the repo
allow_any_instance_of(described_class).to receive(:uri_type).and_return(:raw) allow_any_instance_of(described_class).to receive(:uri_type).and_return(:raw)
allow_any_instance_of(described_class).to receive(:get_uri_types).and_return({ path: :tree })
doc = filter(link('files/images/logo-black.png')) doc = filter(link(path))
expect(doc.at_css('a')['href']) expect(doc.at_css('a')['href'])
.to eq "/#{project_path}/raw/#{ref_escaped}/files/images/logo-black.png" .to eq "/#{project_path}/raw/#{ref_escaped}/files/images/logo-black.png"
......
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