BigW Consortium Gitlab

Commit d85c0eb0 by Jarka Kadlecova

Merge branch '10-0-stable-patch-2' into '10-0-stable'

Prepare 10.0.2 release See merge request gitlab-org/gitlab-ce!14499
parents 2417795b 88540205
......@@ -2,6 +2,7 @@
/* global Flash, Autosave */
import { mapActions, mapGetters } from 'vuex';
import _ from 'underscore';
import autosize from 'vendor/autosize';
import '../../autosave';
import TaskList from '../../task_list';
import * as constants from '../constants';
......@@ -96,6 +97,8 @@
methods: {
...mapActions([
'saveNote',
'stopPolling',
'restartPolling',
'removePlaceholderNotes',
]),
setIsSubmitButtonDisabled(note, isSubmitting) {
......@@ -124,10 +127,14 @@
}
this.isSubmitting = true;
this.note = ''; // Empty textarea while being requested. Repopulate in catch
this.resizeTextarea();
this.stopPolling();
this.saveNote(noteData)
.then((res) => {
this.isSubmitting = false;
this.restartPolling();
if (res.errors) {
if (res.errors.commands_only) {
this.discard();
......@@ -174,6 +181,8 @@
if (shouldClear) {
this.note = '';
this.resizeTextarea();
this.$refs.markdownField.previewMarkdown = false;
}
// reset autostave
......@@ -205,6 +214,11 @@
selector: '.notes',
});
},
resizeTextarea() {
this.$nextTick(() => {
autosize.update(this.$refs.textarea);
});
},
},
mounted() {
// jQuery is needed here because it is a custom event being dispatched with jQuery.
......@@ -247,7 +261,8 @@
:markdown-docs-path="markdownDocsPath"
:quick-actions-docs-path="quickActionsDocsPath"
:add-spacing-classes="false"
:is-confidential-issue="isConfidentialIssue">
:is-confidential-issue="isConfidentialIssue"
ref="markdownField">
<textarea
id="note-body"
name="note[note]"
......
......@@ -186,6 +186,14 @@ export const poll = ({ commit, state, getters }) => {
});
};
export const stopPolling = () => {
eTagPoll.stop();
};
export const restartPolling = () => {
eTagPoll.restart();
};
export const fetchData = ({ commit, state, getters }) => {
const requestData = { endpoint: state.notesData.notesPath, lastFetchedAt: state.lastFetchedAt };
......
......@@ -5,15 +5,19 @@ import * as constants from '../constants';
export default {
[types.ADD_NEW_NOTE](state, note) {
const { discussion_id, type } = note;
const noteData = {
expanded: true,
id: discussion_id,
individual_note: !(type === constants.DISCUSSION_NOTE),
notes: [note],
reply_id: discussion_id,
};
state.notes.push(noteData);
const [exists] = state.notes.filter(n => n.id === note.discussion_id);
if (!exists) {
const noteData = {
expanded: true,
id: discussion_id,
individual_note: !(type === constants.DISCUSSION_NOTE),
notes: [note],
reply_id: discussion_id,
};
state.notes.push(noteData);
}
},
[types.ADD_NEW_REPLY_TO_DISCUSSION](state, note) {
......
......@@ -779,6 +779,14 @@
white-space: normal;
width: 100%;
&.dropdown-menu-user-link {
white-space: nowrap;
.dropdown-menu-user-username {
display: block;
}
}
// make sure the text color is not overriden
&.text-danger {
color: $brand-danger;
......
......@@ -6,7 +6,7 @@
// Header
header.navbar-gitlab-new {
background: linear-gradient(to right, $color-900, $color-800);
background-color: $color-900;
.navbar-collapse {
color: $color-200;
......@@ -201,7 +201,7 @@ body {
@include gitlab-theme($theme-gray-900, $theme-gray-700, $theme-gray-800, $theme-gray-700, $theme-gray-700, $theme-gray-100, $theme-gray-700);
header.navbar-gitlab-new {
background: $theme-gray-100;
background-color: $theme-gray-100;
box-shadow: 0 2px 0 0 $border-color;
.logo-text svg {
......
......@@ -13,22 +13,29 @@ module AvatarsHelper
user_name = options[:user].try(:name) || options[:user_name]
avatar_url = options[:url] || avatar_icon(options[:user] || options[:user_email], avatar_size)
has_tooltip = options[:has_tooltip].nil? ? true : options[:has_tooltip]
data_attributes = {}
data_attributes = options[:data] || {}
css_class = %W[avatar s#{avatar_size}].push(*options[:css_class])
if has_tooltip
css_class.push('has-tooltip')
data_attributes = { container: 'body' }
data_attributes[:container] = 'body'
end
image_tag(
avatar_url,
if options[:lazy]
css_class << 'lazy'
data_attributes[:src] = avatar_url
avatar_url = LazyImageTagHelper.placeholder_image
end
image_options = {
alt: "#{user_name}'s avatar",
src: avatar_url,
data: data_attributes,
class: css_class,
alt: "#{user_name}'s avatar",
title: user_name,
data: data_attributes,
lazy: true
)
title: user_name
}
tag(:img, image_options)
end
def user_avatar(options = {})
......
......@@ -174,7 +174,7 @@ module Ci
end
def assignable_for?(project)
!locked? || projects.exists?(id: project.id)
is_shared? || projects.exists?(id: project.id)
end
def accepting_tags?(build)
......
- access = note_max_access_for_user(note)
- if note.has_special_role?(Note::SpecialRole::FIRST_TIME_CONTRIBUTOR)
%span.note-role.note-role-special.has-tooltip{ title: _("This is the author's first Merge Request to this project. Handle with care.") }
= issuable_first_contribution_icon
- if access = note_max_access_for_user(note)
- if access.nonzero?
%span.note-role.note-role-access= Gitlab::Access.human_access(access)
- if note.resolvable?
......
......@@ -4,7 +4,7 @@
%li.filter-dropdown-item{ class: ('js-current-user' if user == current_user) }
%button.btn.btn-link.dropdown-user{ type: :button }
.avatar-container.s40
= user_avatar_without_link(user: user, lazy: avatar[:lazy], url: avatar[:url], size: 40, has_tooltip: false).gsub('/images/{{avatar_url}}','{{avatar_url}}').html_safe
= user_avatar_without_link(user: user, lazy: avatar[:lazy], url: avatar[:url], size: 40, has_tooltip: false)
.dropdown-user-details
%span
= user.name
......
---
title: Notes will not show an empty bubble when the author isn't a member.
merge_request: 14450
author:
type: fixed
---
title: Some checks in `rake gitlab:check` were failling with 'undefined method `run_command`'
merge_request: 14469
author:
type: fixed
---
title: Make locked setting of Runner to not affect jobs scheduling
merge_request: 14483
author:
type: fixed
---
title: Re-allow `name` attribute on user-provided anchor HTML
merge_request:
author:
type: fixed
......@@ -33,7 +33,7 @@ class MigrateUserExternalMailData < ActiveRecord::Migration
SELECT true
FROM user_synced_attributes_metadata
WHERE user_id = users.id
AND provider = users.email_provider OR (provider IS NULL AND users.email_provider IS NULL)
AND (provider = users.email_provider OR (provider IS NULL AND users.email_provider IS NULL))
)
AND id BETWEEN #{start_id} AND #{end_id}
EOF
......
......@@ -33,7 +33,7 @@ class PostDeployMigrateUserExternalMailData < ActiveRecord::Migration
SELECT true
FROM user_synced_attributes_metadata
WHERE user_id = users.id
AND provider = users.email_provider OR (provider IS NULL AND users.email_provider IS NULL)
AND (provider = users.email_provider OR (provider IS NULL AND users.email_provider IS NULL))
)
AND id BETWEEN #{start_id} AND #{end_id}
EOF
......
......@@ -45,8 +45,9 @@ module Banzai
whitelist[:elements].push('abbr')
whitelist[:attributes]['abbr'] = %w(title)
# Disallow `name` attribute globally
# Disallow `name` attribute globally, allow on `a`
whitelist[:attributes][:all].delete('name')
whitelist[:attributes]['a'].push('name')
# Allow any protocol in `a` elements...
whitelist[:protocols].delete('a')
......
......@@ -32,6 +32,8 @@ module Gitlab
else
blob = repository.lookup(sha)
next unless blob.is_a?(Rugged::Blob)
new(
id: blob.oid,
size: blob.size,
......
......@@ -9,7 +9,7 @@ module SystemCheck
end
def self.current_version
@current_version ||= Gitlab::VersionInfo.parse(run_command(%W(#{Gitlab.config.git.bin_path} --version)))
@current_version ||= Gitlab::VersionInfo.parse(Gitlab::TaskHelpers.run_command(%W(#{Gitlab.config.git.bin_path} --version)))
end
def check?
......
......@@ -9,7 +9,7 @@ module SystemCheck
end
def self.current_version
@current_version ||= Gitlab::VersionInfo.parse(run_command(%w(ruby --version)))
@current_version ||= Gitlab::VersionInfo.parse(Gitlab::TaskHelpers.run_command(%w(ruby --version)))
end
def check?
......
......@@ -26,12 +26,13 @@ describe AvatarsHelper do
subject { helper.user_avatar_without_link(options) }
it 'displays user avatar' do
is_expected.to eq image_tag(
LazyImageTagHelper.placeholder_image,
class: 'avatar s16 has-tooltip lazy',
is_expected.to eq tag(
:img,
alt: "#{user.name}'s avatar",
title: user.name,
data: { container: 'body', src: avatar_icon(user, 16) }
src: avatar_icon(user, 16),
data: { container: 'body' },
class: 'avatar s16 has-tooltip',
title: user.name
)
end
......@@ -39,12 +40,13 @@ describe AvatarsHelper do
let(:options) { { user: user, css_class: '.cat-pics' } }
it 'uses provided css_class' do
is_expected.to eq image_tag(
LazyImageTagHelper.placeholder_image,
class: "avatar s16 #{options[:css_class]} has-tooltip lazy",
is_expected.to eq tag(
:img,
alt: "#{user.name}'s avatar",
title: user.name,
data: { container: 'body', src: avatar_icon(user, 16) }
src: avatar_icon(user, 16),
data: { container: 'body' },
class: "avatar s16 #{options[:css_class]} has-tooltip",
title: user.name
)
end
end
......@@ -53,12 +55,13 @@ describe AvatarsHelper do
let(:options) { { user: user, size: 99 } }
it 'uses provided size' do
is_expected.to eq image_tag(
LazyImageTagHelper.placeholder_image,
class: "avatar s#{options[:size]} has-tooltip lazy",
is_expected.to eq tag(
:img,
alt: "#{user.name}'s avatar",
title: user.name,
data: { container: 'body', src: avatar_icon(user, options[:size]) }
src: avatar_icon(user, options[:size]),
data: { container: 'body' },
class: "avatar s#{options[:size]} has-tooltip",
title: user.name
)
end
end
......@@ -67,12 +70,28 @@ describe AvatarsHelper do
let(:options) { { user: user, url: '/over/the/rainbow.png' } }
it 'uses provided url' do
is_expected.to eq image_tag(
LazyImageTagHelper.placeholder_image,
class: 'avatar s16 has-tooltip lazy',
is_expected.to eq tag(
:img,
alt: "#{user.name}'s avatar",
title: user.name,
data: { container: 'body', src: options[:url] }
src: options[:url],
data: { container: 'body' },
class: "avatar s16 has-tooltip",
title: user.name
)
end
end
context 'with lazy parameter' do
let(:options) { { user: user, lazy: true } }
it 'adds `lazy` class to class list, sets `data-src` with avatar URL and `src` with placeholder image' do
is_expected.to eq tag(
:img,
alt: "#{user.name}'s avatar",
src: LazyImageTagHelper.placeholder_image,
data: { container: 'body', src: avatar_icon(user, 16) },
class: "avatar s16 has-tooltip lazy",
title: user.name
)
end
end
......@@ -82,12 +101,13 @@ describe AvatarsHelper do
let(:options) { { user: user, has_tooltip: true } }
it 'adds has-tooltip' do
is_expected.to eq image_tag(
LazyImageTagHelper.placeholder_image,
class: 'avatar s16 has-tooltip lazy',
is_expected.to eq tag(
:img,
alt: "#{user.name}'s avatar",
title: user.name,
data: { container: 'body', src: avatar_icon(user, 16) }
src: avatar_icon(user, 16),
data: { container: 'body' },
class: "avatar s16 has-tooltip",
title: user.name
)
end
end
......@@ -96,12 +116,12 @@ describe AvatarsHelper do
let(:options) { { user: user, has_tooltip: false } }
it 'does not add has-tooltip or data container' do
is_expected.to eq image_tag(
LazyImageTagHelper.placeholder_image,
class: 'avatar s16 lazy',
is_expected.to eq tag(
:img,
alt: "#{user.name}'s avatar",
title: user.name,
data: { src: avatar_icon(user, 16) }
src: avatar_icon(user, 16),
class: "avatar s16",
title: user.name
)
end
end
......@@ -114,23 +134,25 @@ describe AvatarsHelper do
let(:options) { { user: user, user_name: 'Tinky Winky' } }
it 'prefers user parameter' do
is_expected.to eq image_tag(
LazyImageTagHelper.placeholder_image,
class: 'avatar s16 has-tooltip lazy',
is_expected.to eq tag(
:img,
alt: "#{user.name}'s avatar",
title: user.name,
data: { container: 'body', src: avatar_icon(user, 16) }
src: avatar_icon(user, 16),
data: { container: 'body' },
class: "avatar s16 has-tooltip",
title: user.name
)
end
end
it 'uses user_name and user_email parameter if user is not present' do
is_expected.to eq image_tag(
LazyImageTagHelper.placeholder_image,
class: 'avatar s16 has-tooltip lazy',
is_expected.to eq tag(
:img,
alt: "#{options[:user_name]}'s avatar",
title: options[:user_name],
data: { container: 'body', src: avatar_icon(options[:user_email], 16) }
src: avatar_icon(options[:user_email], 16),
data: { container: 'body' },
class: "avatar s16 has-tooltip",
title: options[:user_name]
)
end
end
......
import Vue from 'vue';
import autosize from 'vendor/autosize';
import store from '~/notes/stores';
import issueCommentForm from '~/notes/components/issue_comment_form.vue';
import { loggedOutIssueData, notesDataMock, userDataMock, issueDataMock } from '../mock_data';
......@@ -55,6 +56,19 @@ describe('issue_comment_form component', () => {
expect(vm.$el.querySelector(`a[href="${quickActionsDocsPath}"]`).textContent.trim()).toEqual('quick actions');
});
it('should resize textarea after note discarded', (done) => {
spyOn(autosize, 'update');
spyOn(vm, 'discard').and.callThrough();
vm.note = 'foo';
vm.discard();
Vue.nextTick(() => {
expect(autosize.update).toHaveBeenCalled();
done();
});
});
describe('edit mode', () => {
it('should enter edit mode when arrow up is pressed', () => {
spyOn(vm, 'editCurrentUserLastNote').and.callThrough();
......
import * as actions from '~/notes/stores/actions';
import testAction from './helpers';
import { discussionMock, notesDataMock, userDataMock, issueDataMock, individualNote } from '../mock_data';
......
......@@ -3,19 +3,31 @@ import { note, discussionMock, notesDataMock, userDataMock, issueDataMock, indiv
describe('Mutation Notes Store', () => {
describe('ADD_NEW_NOTE', () => {
it('should add a new note to an array of notes', () => {
const state = { notes: [] };
let state;
let noteData;
beforeEach(() => {
state = { notes: [] };
noteData = {
expanded: true,
id: note.discussion_id,
individual_note: true,
notes: [note],
reply_id: note.discussion_id,
};
mutations.ADD_NEW_NOTE(state, note);
});
it('should add a new note to an array of notes', () => {
expect(state).toEqual({
notes: [{
expanded: true,
id: note.discussion_id,
individual_note: true,
notes: [note],
reply_id: note.discussion_id,
}],
notes: [noteData],
});
expect(state.notes.length).toBe(1);
});
it('should not add the same note to the notes array', () => {
mutations.ADD_NEW_NOTE(state, note);
expect(state.notes.length).toBe(1);
});
});
......
......@@ -47,9 +47,11 @@ describe Banzai::Filter::SanitizationFilter do
describe 'custom whitelist' do
it 'customizes the whitelist only once' do
instance = described_class.new('Foo')
control_count = instance.whitelist[:transformers].size
3.times { instance.whitelist }
expect(instance.whitelist[:transformers].size).to eq 5
expect(instance.whitelist[:transformers].size).to eq control_count
end
it 'sanitizes `class` attribute from all elements' do
......@@ -101,16 +103,18 @@ describe Banzai::Filter::SanitizationFilter do
expect(filter(act).to_html).to eq exp
end
it 'disallows the `name` attribute globally' do
it 'disallows the `name` attribute globally, allows on `a`' do
html = <<~HTML
<img name="getElementById" src="">
<span name="foo" class="bar">Hi</span>
<a name="foo" class="bar">Bye</a>
HTML
doc = filter(html)
expect(doc.at_css('img')).not_to have_attribute('name')
expect(doc.at_css('span')).not_to have_attribute('name')
expect(doc.at_css('a')).to have_attribute('name')
end
it 'allows `summary` elements' do
......
......@@ -119,10 +119,13 @@ describe Gitlab::Git::Blob, seed_helper: true do
shared_examples 'finding blobs by ID' do
let(:raw_blob) { Gitlab::Git::Blob.raw(repository, SeedRepo::RubyBlob::ID) }
let(:bad_blob) { Gitlab::Git::Blob.raw(repository, SeedRepo::BigCommit::ID) }
it { expect(raw_blob.id).to eq(SeedRepo::RubyBlob::ID) }
it { expect(raw_blob.data[0..10]).to eq("require \'fi") }
it { expect(raw_blob.size).to eq(669) }
it { expect(raw_blob.truncated?).to be_falsey }
it { expect(bad_blob).to be_nil }
context 'large file' do
it 'limits the size of a large file' do
......
require 'spec_helper'
describe SystemCheck::BaseCheck do
context 'helpers on instance level' do
it 'responds to SystemCheck::Helpers methods' do
expect(subject).to respond_to :fix_and_rerun, :for_more_information, :see_installation_guide_section,
:finished_checking, :start_checking, :try_fixing_it, :sanitized_message, :should_sanitize?, :omnibus_gitlab?,
:sudo_gitlab
end
it 'responds to Gitlab::TaskHelpers methods' do
expect(subject).to respond_to :ask_to_continue, :os_name, :prompt, :run_and_match, :run_command,
:run_command!, :uid_for, :gid_for, :gitlab_user, :gitlab_user?, :warn_user_is_not_gitlab, :all_repos,
:repository_storage_paths_args, :user_home, :checkout_or_clone_version, :clone_repo, :checkout_version
end
end
end
......@@ -183,75 +183,42 @@ describe Ci::Runner do
end
end
context 'when runner is locked' do
context 'when runner is shared' do
before do
runner.locked = true
runner.is_shared = true
build.project.runners = []
end
shared_examples 'locked build picker' do
context 'when runner cannot pick untagged jobs' do
before do
runner.run_untagged = false
end
it 'can handle builds' do
expect(runner.can_pick?(build)).to be_truthy
end
it 'cannot handle builds without tags' do
expect(runner.can_pick?(build)).to be_falsey
end
context 'when runner is locked' do
before do
runner.locked = true
end
context 'when having runner tags' do
before do
runner.tag_list = %w(bb cc)
end
it 'cannot handle it for builds without matching tags' do
build.tag_list = ['aa']
expect(runner.can_pick?(build)).to be_falsey
end
it 'can handle builds' do
expect(runner.can_pick?(build)).to be_truthy
end
end
end
context 'when serving the same project' do
it 'can handle it' do
context 'when runner is not shared' do
context 'when runner is assigned to a project' do
it 'can handle builds' do
expect(runner.can_pick?(build)).to be_truthy
end
it_behaves_like 'locked build picker'
context 'when having runner tags' do
before do
runner.tag_list = %w(bb cc)
build.tag_list = ['bb']
end
it 'can handle it for matching tags' do
expect(runner.can_pick?(build)).to be_truthy
end
end
end
context 'serving a different project' do
context 'when runner is not assigned to a project' do
before do
runner.runner_projects.destroy_all
build.project.runners = []
end
it 'cannot handle it' do
it 'cannot handle builds' do
expect(runner.can_pick?(build)).to be_falsey
end
it_behaves_like 'locked build picker'
context 'when having runner tags' do
before do
runner.tag_list = %w(bb cc)
build.tag_list = ['bb']
end
it 'cannot handle it for matching tags' do
expect(runner.can_pick?(build)).to be_falsey
end
end
end
end
......
......@@ -75,4 +75,24 @@ describe Gitlab::TaskHelpers do
subject.checkout_version(tag, clone_path)
end
end
describe '#run_command' do
it 'runs command and return the output' do
expect(subject.run_command(%w(echo it works!))).to eq("it works!\n")
end
it 'returns empty string when command doesnt exist' do
expect(subject.run_command(%w(nonexistentcommand with arguments))).to eq('')
end
end
describe '#run_command!' do
it 'runs command and return the output' do
expect(subject.run_command!(%w(echo it works!))).to eq("it works!\n")
end
it 'returns and exception when command exit with non zero code' do
expect { subject.run_command!(['bash', '-c', 'exit 1']) }.to raise_error Gitlab::TaskFailedError
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