Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
80e5134020
commit
a048261403
|
@ -1,9 +1,9 @@
|
|||
/* eslint-disable no-param-reassign, no-void, consistent-return */
|
||||
/* eslint-disable no-param-reassign, consistent-return */
|
||||
|
||||
import AccessorUtilities from './lib/utils/accessor';
|
||||
|
||||
export default class Autosave {
|
||||
constructor(field, key) {
|
||||
constructor(field, key, fallbackKey) {
|
||||
this.field = field;
|
||||
|
||||
this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe();
|
||||
|
@ -11,6 +11,7 @@ export default class Autosave {
|
|||
key = key.join('/');
|
||||
}
|
||||
this.key = `autosave/${key}`;
|
||||
this.fallbackKey = fallbackKey;
|
||||
this.field.data('autosave', this);
|
||||
this.restore();
|
||||
this.field.on('input', () => this.save());
|
||||
|
@ -21,9 +22,12 @@ export default class Autosave {
|
|||
if (!this.field.length) return;
|
||||
|
||||
const text = window.localStorage.getItem(this.key);
|
||||
const fallbackText = window.localStorage.getItem(this.fallbackKey);
|
||||
|
||||
if ((text != null ? text.length : void 0) > 0) {
|
||||
if (text) {
|
||||
this.field.val(text);
|
||||
} else if (fallbackText) {
|
||||
this.field.val(fallbackText);
|
||||
}
|
||||
|
||||
this.field.trigger('input');
|
||||
|
@ -41,7 +45,10 @@ export default class Autosave {
|
|||
|
||||
const text = this.field.val();
|
||||
|
||||
if (this.isLocalStorageAvailable && (text != null ? text.length : void 0) > 0) {
|
||||
if (this.isLocalStorageAvailable && text) {
|
||||
if (this.fallbackKey) {
|
||||
window.localStorage.setItem(this.fallbackKey, text);
|
||||
}
|
||||
return window.localStorage.setItem(this.key, text);
|
||||
}
|
||||
|
||||
|
@ -51,6 +58,7 @@ export default class Autosave {
|
|||
reset() {
|
||||
if (!this.isLocalStorageAvailable) return;
|
||||
|
||||
window.localStorage.removeItem(this.fallbackKey);
|
||||
return window.localStorage.removeItem(this.key);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<script>
|
||||
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import { GlDropdown, GlDropdownItem, GlIcon } from '@gitlab/ui';
|
||||
import { s__ } from '../../locale';
|
||||
|
||||
export default {
|
||||
|
@ -8,7 +7,7 @@ export default {
|
|||
components: {
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
Icon,
|
||||
GlIcon,
|
||||
},
|
||||
props: {
|
||||
stacks: {
|
||||
|
@ -86,8 +85,9 @@ export default {
|
|||
href="https://crossplane.io/docs/master/stacks-guide.html"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>{{ __('Crossplane') }}</a
|
||||
>
|
||||
>{{ __('Crossplane') }}
|
||||
<gl-icon name="external-link" class="vertical-align-middle" />
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -6,6 +6,36 @@ import UsersSelect from './users_select';
|
|||
import ZenMode from './zen_mode';
|
||||
import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select';
|
||||
import { parsePikadayDate, pikadayToString } from './lib/utils/datetime_utility';
|
||||
import { queryToObject, objectToQuery } from './lib/utils/url_utility';
|
||||
|
||||
function organizeQuery(obj, isFallbackKey = false) {
|
||||
const sourceBranch = 'merge_request[source_branch]';
|
||||
const targetBranch = 'merge_request[target_branch]';
|
||||
|
||||
if (isFallbackKey) {
|
||||
return {
|
||||
[sourceBranch]: obj[sourceBranch],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
[sourceBranch]: obj[sourceBranch],
|
||||
[targetBranch]: obj[targetBranch],
|
||||
};
|
||||
}
|
||||
|
||||
function format(searchTerm, isFallbackKey = false) {
|
||||
const queryObject = queryToObject(searchTerm);
|
||||
const organizeQueryObject = organizeQuery(queryObject, isFallbackKey);
|
||||
const formattedQuery = objectToQuery(organizeQueryObject);
|
||||
|
||||
return formattedQuery;
|
||||
}
|
||||
|
||||
function getFallbackKey() {
|
||||
const searchTerm = format(document.location.search, true);
|
||||
return ['autosave', document.location.pathname, searchTerm].join('/');
|
||||
}
|
||||
|
||||
export default class IssuableForm {
|
||||
constructor(form) {
|
||||
|
@ -57,16 +87,20 @@ export default class IssuableForm {
|
|||
}
|
||||
|
||||
initAutosave() {
|
||||
this.autosave = new Autosave(this.titleField, [
|
||||
document.location.pathname,
|
||||
document.location.search,
|
||||
'title',
|
||||
]);
|
||||
return new Autosave(this.descriptionField, [
|
||||
document.location.pathname,
|
||||
document.location.search,
|
||||
'description',
|
||||
]);
|
||||
const searchTerm = format(document.location.search);
|
||||
const fallbackKey = getFallbackKey();
|
||||
|
||||
this.autosave = new Autosave(
|
||||
this.titleField,
|
||||
[document.location.pathname, searchTerm, 'title'],
|
||||
`${fallbackKey}=title`,
|
||||
);
|
||||
|
||||
return new Autosave(
|
||||
this.descriptionField,
|
||||
[document.location.pathname, searchTerm, 'description'],
|
||||
`${fallbackKey}=description`,
|
||||
);
|
||||
}
|
||||
|
||||
handleSubmit() {
|
||||
|
|
|
@ -181,4 +181,36 @@ export function getWebSocketUrl(path) {
|
|||
return `${getWebSocketProtocol()}//${joinPaths(window.location.host, path)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert search query into an object
|
||||
*
|
||||
* @param {String} query from "document.location.search"
|
||||
* @returns {Object}
|
||||
*
|
||||
* ex: "?one=1&two=2" into {one: 1, two: 2}
|
||||
*/
|
||||
export function queryToObject(query) {
|
||||
const removeQuestionMarkFromQuery = String(query).startsWith('?') ? query.slice(1) : query;
|
||||
return removeQuestionMarkFromQuery.split('&').reduce((accumulator, curr) => {
|
||||
const p = curr.split('=');
|
||||
accumulator[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
|
||||
return accumulator;
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert search query object back into a search query
|
||||
*
|
||||
* @param {Object} obj that needs to be converted
|
||||
* @returns {String}
|
||||
*
|
||||
* ex: {one: 1, two: 2} into "one=1&two=2"
|
||||
*
|
||||
*/
|
||||
export function objectToQuery(obj) {
|
||||
return Object.keys(obj)
|
||||
.map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`)
|
||||
.join('&');
|
||||
}
|
||||
|
||||
export { joinPaths };
|
||||
|
|
|
@ -30,9 +30,6 @@ export default {
|
|||
return this.collapseGroup && this.showGroup ? 'angle-down' : 'angle-right';
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.showGroup = this.collapseGroup;
|
||||
},
|
||||
methods: {
|
||||
collapse() {
|
||||
this.showGroup = !this.showGroup;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable func-names, no-var, no-return-assign */
|
||||
/* eslint-disable func-names, no-return-assign */
|
||||
|
||||
import $ from 'jquery';
|
||||
import Cookies from 'js-cookie';
|
||||
|
@ -90,19 +90,19 @@ export default class Project {
|
|||
}
|
||||
|
||||
static initRefSwitcher() {
|
||||
var refListItem = document.createElement('li');
|
||||
var refLink = document.createElement('a');
|
||||
const refListItem = document.createElement('li');
|
||||
const refLink = document.createElement('a');
|
||||
|
||||
refLink.href = '#';
|
||||
|
||||
return $('.js-project-refs-dropdown').each(function() {
|
||||
var $dropdown = $(this);
|
||||
var selected = $dropdown.data('selected');
|
||||
var fieldName = $dropdown.data('fieldName');
|
||||
var shouldVisit = Boolean($dropdown.data('visit'));
|
||||
var $form = $dropdown.closest('form');
|
||||
var action = $form.attr('action');
|
||||
var linkTarget = mergeUrlParams(serializeForm($form[0]), action);
|
||||
const $dropdown = $(this);
|
||||
const selected = $dropdown.data('selected');
|
||||
const fieldName = $dropdown.data('fieldName');
|
||||
const shouldVisit = Boolean($dropdown.data('visit'));
|
||||
const $form = $dropdown.closest('form');
|
||||
const action = $form.attr('action');
|
||||
const linkTarget = mergeUrlParams(serializeForm($form[0]), action);
|
||||
|
||||
return $dropdown.glDropdown({
|
||||
data(term, callback) {
|
||||
|
@ -123,9 +123,9 @@ export default class Project {
|
|||
inputFieldName: $dropdown.data('inputFieldName'),
|
||||
fieldName,
|
||||
renderRow(ref) {
|
||||
var li = refListItem.cloneNode(false);
|
||||
const li = refListItem.cloneNode(false);
|
||||
|
||||
var link = refLink.cloneNode(false);
|
||||
const link = refLink.cloneNode(false);
|
||||
|
||||
if (ref === selected) {
|
||||
link.className = 'is-active';
|
||||
|
|
|
@ -88,10 +88,6 @@ class DiffNote < Note
|
|||
line&.suggestible?
|
||||
end
|
||||
|
||||
def discussion_first_note?
|
||||
self == discussion.first_note
|
||||
end
|
||||
|
||||
def banzai_render_context(field)
|
||||
super.merge(suggestions_filter_enabled: true)
|
||||
end
|
||||
|
@ -108,7 +104,7 @@ class DiffNote < Note
|
|||
end
|
||||
|
||||
def should_create_diff_file?
|
||||
on_text? && note_diff_file.nil? && discussion_first_note?
|
||||
on_text? && note_diff_file.nil? && start_of_discussion?
|
||||
end
|
||||
|
||||
def fetch_diff_file
|
||||
|
|
|
@ -139,10 +139,6 @@ class Discussion
|
|||
false
|
||||
end
|
||||
|
||||
def new_discussion?
|
||||
notes.length == 1
|
||||
end
|
||||
|
||||
def last_note
|
||||
@last_note ||= notes.last
|
||||
end
|
||||
|
|
|
@ -409,6 +409,10 @@ class Note < ApplicationRecord
|
|||
full_discussion || to_discussion
|
||||
end
|
||||
|
||||
def start_of_discussion?
|
||||
discussion.first_note == self
|
||||
end
|
||||
|
||||
def part_of_discussion?
|
||||
!to_discussion.individual_note?
|
||||
end
|
||||
|
|
|
@ -7,20 +7,24 @@ module Issuable
|
|||
def execute(issuable, old_labels: [], is_update: true)
|
||||
@issuable = issuable
|
||||
|
||||
if is_update
|
||||
if issuable.previous_changes.include?('title')
|
||||
create_title_change_note(issuable.previous_changes['title'].first)
|
||||
# We disable touch so that created system notes do not update
|
||||
# the noteable's updated_at field
|
||||
ActiveRecord::Base.no_touching do
|
||||
if is_update
|
||||
if issuable.previous_changes.include?('title')
|
||||
create_title_change_note(issuable.previous_changes['title'].first)
|
||||
end
|
||||
|
||||
handle_description_change_note
|
||||
|
||||
handle_time_tracking_note if issuable.is_a?(TimeTrackable)
|
||||
create_discussion_lock_note if issuable.previous_changes.include?('discussion_locked')
|
||||
end
|
||||
|
||||
handle_description_change_note
|
||||
|
||||
handle_time_tracking_note if issuable.is_a?(TimeTrackable)
|
||||
create_discussion_lock_note if issuable.previous_changes.include?('discussion_locked')
|
||||
create_due_date_note if issuable.previous_changes.include?('due_date')
|
||||
create_milestone_note if issuable.previous_changes.include?('milestone_id')
|
||||
create_labels_note(old_labels) if old_labels && issuable.labels != old_labels
|
||||
end
|
||||
|
||||
create_due_date_note if issuable.previous_changes.include?('due_date')
|
||||
create_milestone_note if issuable.previous_changes.include?('milestone_id')
|
||||
create_labels_note(old_labels) if old_labels && issuable.labels != old_labels
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -164,9 +164,7 @@ class IssuableBaseService < BaseService
|
|||
before_create(issuable)
|
||||
|
||||
if issuable.save
|
||||
ActiveRecord::Base.no_touching do
|
||||
Issuable::CommonSystemNotesService.new(project, current_user).execute(issuable, is_update: false)
|
||||
end
|
||||
Issuable::CommonSystemNotesService.new(project, current_user).execute(issuable, is_update: false)
|
||||
|
||||
after_create(issuable)
|
||||
execute_hooks(issuable)
|
||||
|
@ -227,10 +225,7 @@ class IssuableBaseService < BaseService
|
|||
ensure_milestone_available(issuable)
|
||||
|
||||
if issuable.with_transaction_returning_status { issuable.save(touch: should_touch) }
|
||||
# We do not touch as it will affect a update on updated_at field
|
||||
ActiveRecord::Base.no_touching do
|
||||
Issuable::CommonSystemNotesService.new(project, current_user).execute(issuable, old_labels: old_associations[:labels])
|
||||
end
|
||||
Issuable::CommonSystemNotesService.new(project, current_user).execute(issuable, old_labels: old_associations[:labels])
|
||||
|
||||
handle_changes(issuable, old_associations: old_associations)
|
||||
|
||||
|
@ -264,10 +259,7 @@ class IssuableBaseService < BaseService
|
|||
before_update(issuable, skip_spam_check: true)
|
||||
|
||||
if issuable.with_transaction_returning_status { issuable.save }
|
||||
# We do not touch as it will affect a update on updated_at field
|
||||
ActiveRecord::Base.no_touching do
|
||||
Issuable::CommonSystemNotesService.new(project, current_user).execute(issuable, old_labels: nil)
|
||||
end
|
||||
Issuable::CommonSystemNotesService.new(project, current_user).execute(issuable, old_labels: nil)
|
||||
|
||||
handle_task_changes(issuable)
|
||||
invalidate_cache_counts(issuable, users: issuable.assignees.to_a)
|
||||
|
|
|
@ -4,7 +4,7 @@ module Notes
|
|||
class BaseService < ::BaseService
|
||||
def clear_noteable_diffs_cache(note)
|
||||
if note.is_a?(DiffNote) &&
|
||||
note.discussion_first_note? &&
|
||||
note.start_of_discussion? &&
|
||||
note.position.unfolded_diff?(project.repository)
|
||||
note.noteable.diffs.clear_cache
|
||||
end
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
- if discussion.nil?
|
||||
commented
|
||||
- else
|
||||
- if discussion.new_discussion?
|
||||
- if note.start_of_discussion?
|
||||
started a new
|
||||
- else
|
||||
commented on a
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<% if discussion.nil? -%>
|
||||
<%= 'commented' -%>:
|
||||
<% else -%>
|
||||
<% if discussion.new_discussion? -%>
|
||||
<% if note.start_of_discussion? -%>
|
||||
<%= 'started a new discussion' -%>
|
||||
<% else -%>
|
||||
<%= 'commented on a discussion' -%>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Keep details in MR when changing target branch
|
||||
merge_request: 19138
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix graph groups in monitor dashboard that are hidden on load
|
||||
merge_request: 20312
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: SaaS trial copy shows plan
|
||||
merge_request: 20207
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add missing external-link icon for Crossplane managed app
|
||||
merge_request: 20283
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: View closed issues in epic
|
||||
merge_request: 19741
|
||||
author:
|
||||
type: added
|
|
@ -2515,10 +2515,10 @@ msgstr ""
|
|||
msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
|
||||
msgstr ""
|
||||
|
||||
msgid "BillingPlans|Your GitLab.com Gold trial expired on %{expiration_date}. You can restore access to the Gold features at any time by upgrading below."
|
||||
msgid "BillingPlans|Your GitLab.com %{plan} trial will <strong>expire after %{expiration_date}</strong>. You can retain access to the %{plan} features by upgrading below."
|
||||
msgstr ""
|
||||
|
||||
msgid "BillingPlans|Your GitLab.com Gold trial will <strong>expire after %{expiration_date}</strong>. You can retain access to the Gold features by upgrading below."
|
||||
msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. You can restore access to the features at any time by upgrading below."
|
||||
msgstr ""
|
||||
|
||||
msgid "BillingPlans|billed annually at %{price_per_year}"
|
||||
|
@ -5770,7 +5770,7 @@ msgstr ""
|
|||
msgid "DesignManagement|Could not add a new comment. Please try again"
|
||||
msgstr ""
|
||||
|
||||
msgid "DesignManagement|Could not create new discussion, please try again."
|
||||
msgid "DesignManagement|Could not create new discussion. Please try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "DesignManagement|Could not find design, please try again."
|
||||
|
@ -5821,9 +5821,6 @@ msgstr ""
|
|||
msgid "DesignManagement|We could not delete %{design}. Please try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "DesignManagement|We could not delete design(s). Please try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "Designs"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -75,9 +75,11 @@ class AutomatedCleanup
|
|||
deployed_at = Time.parse(last_deploy)
|
||||
|
||||
if deployed_at < delete_threshold
|
||||
delete_environment(environment, deployment)
|
||||
release = Quality::HelmClient::Release.new(environment.slug, 1, deployed_at.to_s, nil, nil, review_apps_namespace)
|
||||
releases_to_delete << release
|
||||
environment = delete_environment(environment, deployment)
|
||||
if environment
|
||||
release = Quality::HelmClient::Release.new(environment.slug, 1, deployed_at.to_s, nil, nil, review_apps_namespace)
|
||||
releases_to_delete << release
|
||||
end
|
||||
elsif deployed_at < stop_threshold
|
||||
stop_environment(environment, deployment)
|
||||
else
|
||||
|
@ -116,11 +118,17 @@ class AutomatedCleanup
|
|||
def delete_environment(environment, deployment)
|
||||
print_release_state(subject: 'Review app', release_name: environment.slug, release_date: deployment.created_at, action: 'deleting')
|
||||
gitlab.delete_environment(project_path, environment.id)
|
||||
|
||||
rescue Gitlab::Error::Forbidden
|
||||
puts "Review app '#{environment.slug}' is forbidden: skipping it"
|
||||
end
|
||||
|
||||
def stop_environment(environment, deployment)
|
||||
print_release_state(subject: 'Review app', release_name: environment.slug, release_date: deployment.created_at, action: 'stopping')
|
||||
gitlab.stop_environment(project_path, environment.id)
|
||||
|
||||
rescue Gitlab::Error::Forbidden
|
||||
puts "Review app '#{environment.slug}' is forbidden: skipping it"
|
||||
end
|
||||
|
||||
def helm_releases
|
||||
|
|
|
@ -9,6 +9,7 @@ describe('Autosave', () => {
|
|||
let autosave;
|
||||
const field = $('<textarea></textarea>');
|
||||
const key = 'key';
|
||||
const fallbackKey = 'fallbackKey';
|
||||
|
||||
describe('class constructor', () => {
|
||||
beforeEach(() => {
|
||||
|
@ -22,6 +23,13 @@ describe('Autosave', () => {
|
|||
expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled();
|
||||
expect(autosave.isLocalStorageAvailable).toBe(true);
|
||||
});
|
||||
|
||||
it('should set .isLocalStorageAvailable if fallbackKey is passed', () => {
|
||||
autosave = new Autosave(field, key, fallbackKey);
|
||||
|
||||
expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled();
|
||||
expect(autosave.isLocalStorageAvailable).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('restore', () => {
|
||||
|
@ -151,4 +159,33 @@ describe('Autosave', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('restore with fallbackKey', () => {
|
||||
beforeEach(() => {
|
||||
autosave = {
|
||||
field,
|
||||
key,
|
||||
fallbackKey,
|
||||
};
|
||||
autosave.isLocalStorageAvailable = true;
|
||||
});
|
||||
|
||||
it('should call .getItem', () => {
|
||||
Autosave.prototype.restore.call(autosave);
|
||||
|
||||
expect(window.localStorage.getItem).toHaveBeenCalledWith(fallbackKey);
|
||||
});
|
||||
|
||||
it('should call .setItem for key and fallbackKey', () => {
|
||||
Autosave.prototype.save.call(autosave);
|
||||
|
||||
expect(window.localStorage.setItem).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should call .removeItem for key and fallbackKey', () => {
|
||||
Autosave.prototype.reset.call(autosave);
|
||||
|
||||
expect(window.localStorage.removeItem).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlDropdownItem } from '@gitlab/ui';
|
||||
import { GlDropdownItem, GlIcon } from '@gitlab/ui';
|
||||
import CrossplaneProviderStack from '~/clusters/components/crossplane_provider_stack.vue';
|
||||
|
||||
describe('CrossplaneProviderStack component', () => {
|
||||
|
@ -72,7 +72,12 @@ describe('CrossplaneProviderStack component', () => {
|
|||
findFirstDropdownElement().vm.$emit('click');
|
||||
expect(wrapper.emitted().set[0][0].code).toEqual('gcp');
|
||||
});
|
||||
it('it renders the correct dropdown text when no stack is selected', () => {
|
||||
|
||||
it('renders the correct dropdown text when no stack is selected', () => {
|
||||
expect(wrapper.vm.dropdownText).toBe('Select Stack');
|
||||
});
|
||||
|
||||
it('renders an external link', () => {
|
||||
expect(wrapper.find(GlIcon).props('name')).toBe('external-link');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -282,4 +282,20 @@ describe('URL utility', () => {
|
|||
expect(urlUtils.getWebSocketUrl(path)).toEqual('ws://example.com/lorem/ipsum?a=bc');
|
||||
});
|
||||
});
|
||||
|
||||
describe('queryToObject', () => {
|
||||
it('converts search query into an object', () => {
|
||||
const searchQuery = '?one=1&two=2';
|
||||
|
||||
expect(urlUtils.queryToObject(searchQuery)).toEqual({ one: '1', two: '2' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('objectToQuery', () => {
|
||||
it('converts search query object back into a search query', () => {
|
||||
const searchQueryObject = { one: '1', two: '2' };
|
||||
|
||||
expect(urlUtils.objectToQuery(searchQueryObject)).toEqual('one=1&two=2');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,47 +1,49 @@
|
|||
import Vue from 'vue';
|
||||
import noteEditedText from '~/notes/components/note_edited_text.vue';
|
||||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import NoteEditedText from '~/notes/components/note_edited_text.vue';
|
||||
|
||||
describe('note_edited_text', () => {
|
||||
let vm;
|
||||
let props;
|
||||
const localVue = createLocalVue();
|
||||
const propsData = {
|
||||
actionText: 'Edited',
|
||||
className: 'foo-bar',
|
||||
editedAt: '2017-08-04T09:52:31.062Z',
|
||||
editedBy: {
|
||||
avatar_url: 'path',
|
||||
id: 1,
|
||||
name: 'Root',
|
||||
path: '/root',
|
||||
state: 'active',
|
||||
username: 'root',
|
||||
},
|
||||
};
|
||||
|
||||
describe('NoteEditedText', () => {
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
const Component = Vue.extend(noteEditedText);
|
||||
props = {
|
||||
actionText: 'Edited',
|
||||
className: 'foo-bar',
|
||||
editedAt: '2017-08-04T09:52:31.062Z',
|
||||
editedBy: {
|
||||
avatar_url: 'path',
|
||||
id: 1,
|
||||
name: 'Root',
|
||||
path: '/root',
|
||||
state: 'active',
|
||||
username: 'root',
|
||||
},
|
||||
};
|
||||
|
||||
vm = new Component({
|
||||
propsData: props,
|
||||
}).$mount();
|
||||
wrapper = shallowMount(NoteEditedText, {
|
||||
localVue,
|
||||
propsData,
|
||||
sync: false,
|
||||
attachToDocument: true,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('should render block with provided className', () => {
|
||||
expect(vm.$el.className).toEqual(props.className);
|
||||
expect(wrapper.classes()).toContain(propsData.className);
|
||||
});
|
||||
|
||||
it('should render provided actionText', () => {
|
||||
expect(vm.$el.textContent).toContain(props.actionText);
|
||||
expect(wrapper.text().trim()).toContain(propsData.actionText);
|
||||
});
|
||||
|
||||
it('should render provided user information', () => {
|
||||
const authorLink = vm.$el.querySelector('.js-user-link');
|
||||
const authorLink = wrapper.find('.js-user-link');
|
||||
|
||||
expect(authorLink.getAttribute('href')).toEqual(props.editedBy.path);
|
||||
expect(authorLink.textContent.trim()).toEqual(props.editedBy.name);
|
||||
expect(authorLink.attributes('href')).toEqual(propsData.editedBy.path);
|
||||
expect(authorLink.text().trim()).toEqual(propsData.editedBy.name);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -39,6 +39,8 @@ describe('Registry List', () => {
|
|||
// See https://github.com/vuejs/vue-test-utils/issues/532.
|
||||
Vue.config.silent = true;
|
||||
wrapper = mount(registry, {
|
||||
attachToDocument: true,
|
||||
sync: false,
|
||||
propsData,
|
||||
computed: {
|
||||
repos() {
|
||||
|
@ -67,6 +69,8 @@ describe('Registry List', () => {
|
|||
let localWrapper;
|
||||
beforeEach(() => {
|
||||
localWrapper = mount(registry, {
|
||||
attachToDocument: true,
|
||||
sync: false,
|
||||
propsData,
|
||||
computed: {
|
||||
repos() {
|
||||
|
|
|
@ -22,7 +22,14 @@ describe('collapsible registry container', () => {
|
|||
const findToggleRepos = (w = wrapper) => w.findAll('.js-toggle-repo');
|
||||
const findDeleteModal = (w = wrapper) => w.find({ ref: 'deleteModal' });
|
||||
|
||||
const mountWithStore = config => mount(collapsibleComponent, { ...config, store, localVue });
|
||||
const mountWithStore = config =>
|
||||
mount(collapsibleComponent, {
|
||||
...config,
|
||||
store,
|
||||
localVue,
|
||||
attachToDocument: true,
|
||||
sync: false,
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
createFlash.mockClear();
|
||||
|
@ -63,12 +70,15 @@ describe('collapsible registry container', () => {
|
|||
it('should be closed by default', () => {
|
||||
expectIsClosed();
|
||||
});
|
||||
it('should be open when user clicks on closed repo', () => {
|
||||
it('should be open when user clicks on closed repo', done => {
|
||||
const toggleRepos = findToggleRepos(wrapper);
|
||||
toggleRepos.at(0).trigger('click');
|
||||
const container = findContainerImageTags(wrapper);
|
||||
expect(container.exists()).toBe(true);
|
||||
expect(wrapper.vm.fetchList).toHaveBeenCalled();
|
||||
Vue.nextTick(() => {
|
||||
const container = findContainerImageTags(wrapper);
|
||||
expect(container.exists()).toBe(true);
|
||||
expect(wrapper.vm.fetchList).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should be closed when the user clicks on an opened repo', done => {
|
||||
const toggleRepos = findToggleRepos(wrapper);
|
||||
|
|
|
@ -6,6 +6,8 @@ describe('Registry Project Empty state', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(projectEmptyState, {
|
||||
attachToDocument: true,
|
||||
sync: false,
|
||||
propsData: {
|
||||
noContainersImage: 'imageUrl',
|
||||
helpPagePath: 'help',
|
||||
|
|
|
@ -28,7 +28,8 @@ describe('table registry', () => {
|
|||
const findImageId = (w = wrapper) => w.find({ ref: 'imageId' });
|
||||
const bulkDeletePath = 'path';
|
||||
|
||||
const mountWithStore = config => mount(tableRegistry, { ...config, store, localVue });
|
||||
const mountWithStore = config =>
|
||||
mount(tableRegistry, { ...config, store, localVue, attachToDocument: true, sync: false });
|
||||
|
||||
beforeEach(() => {
|
||||
// This is needed due to console.error called by vue to emit a warning that stop the tests
|
||||
|
@ -196,7 +197,7 @@ describe('table registry', () => {
|
|||
expect(wrapper.vm.handleSingleDelete).toHaveBeenCalledWith(repoPropsData.list[0]);
|
||||
expect(wrapper.vm.handleMultipleDelete).not.toHaveBeenCalled();
|
||||
});
|
||||
it('on ok when multiple items are selected should call muultiDelete', () => {
|
||||
it('on ok when multiple items are selected should call multiDelete', () => {
|
||||
wrapper.setData({ itemsToBeDeleted: [0, 1, 2] });
|
||||
wrapper.vm.onDeletionConfirmed();
|
||||
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
/* eslint-disable no-var, no-else-return, dot-notation, no-return-assign, no-new, no-underscore-dangle */
|
||||
/* eslint-disable no-else-return, dot-notation, no-return-assign, no-new, no-underscore-dangle */
|
||||
|
||||
import $ from 'jquery';
|
||||
import LineHighlighter from '~/line_highlighter';
|
||||
|
||||
describe('LineHighlighter', function() {
|
||||
var clickLine;
|
||||
preloadFixtures('static/line_highlighter.html');
|
||||
clickLine = function(number, eventData = {}) {
|
||||
const clickLine = function(number, eventData = {}) {
|
||||
if ($.isEmptyObject(eventData)) {
|
||||
return $(`#L${number}`).click();
|
||||
} else {
|
||||
|
@ -39,34 +38,30 @@ describe('LineHighlighter', function() {
|
|||
});
|
||||
|
||||
it('highlights a range of lines given in the URL hash', function() {
|
||||
var line;
|
||||
new LineHighlighter({ hash: '#L5-25' });
|
||||
|
||||
expect($(`.${this.css}`).length).toBe(21);
|
||||
for (line = 5; line <= 25; line += 1) {
|
||||
for (let line = 5; line <= 25; line += 1) {
|
||||
expect($(`#LC${line}`)).toHaveClass(this.css);
|
||||
}
|
||||
});
|
||||
|
||||
it('scrolls to the first highlighted line on initial load', function() {
|
||||
var spy;
|
||||
spy = spyOn($, 'scrollTo');
|
||||
const spy = spyOn($, 'scrollTo');
|
||||
new LineHighlighter({ hash: '#L5-25' });
|
||||
|
||||
expect(spy).toHaveBeenCalledWith('#L5', jasmine.anything());
|
||||
});
|
||||
|
||||
it('discards click events', function() {
|
||||
var spy;
|
||||
spy = spyOnEvent('a[data-line-number]', 'click');
|
||||
const spy = spyOnEvent('a[data-line-number]', 'click');
|
||||
clickLine(13);
|
||||
|
||||
expect(spy).toHaveBeenPrevented();
|
||||
});
|
||||
|
||||
it('handles garbage input from the hash', function() {
|
||||
var func;
|
||||
func = function() {
|
||||
const func = function() {
|
||||
return new LineHighlighter({ fileHolderSelector: '#blob-content-holder' });
|
||||
};
|
||||
|
||||
|
@ -76,8 +71,7 @@ describe('LineHighlighter', function() {
|
|||
|
||||
describe('clickHandler', function() {
|
||||
it('handles clicking on a child icon element', function() {
|
||||
var spy;
|
||||
spy = spyOn(this['class'], 'setHash').and.callThrough();
|
||||
const spy = spyOn(this['class'], 'setHash').and.callThrough();
|
||||
$('#L13 i')
|
||||
.mousedown()
|
||||
.click();
|
||||
|
@ -102,8 +96,7 @@ describe('LineHighlighter', function() {
|
|||
});
|
||||
|
||||
it('sets the hash', function() {
|
||||
var spy;
|
||||
spy = spyOn(this['class'], 'setHash').and.callThrough();
|
||||
const spy = spyOn(this['class'], 'setHash').and.callThrough();
|
||||
clickLine(13);
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(13);
|
||||
|
@ -112,8 +105,7 @@ describe('LineHighlighter', function() {
|
|||
|
||||
describe('with shiftKey', function() {
|
||||
it('sets the hash', function() {
|
||||
var spy;
|
||||
spy = spyOn(this['class'], 'setHash').and.callThrough();
|
||||
const spy = spyOn(this['class'], 'setHash').and.callThrough();
|
||||
clickLine(13);
|
||||
clickLine(20, {
|
||||
shiftKey: true,
|
||||
|
@ -134,8 +126,7 @@ describe('LineHighlighter', function() {
|
|||
});
|
||||
|
||||
it('sets the hash', function() {
|
||||
var spy;
|
||||
spy = spyOn(this['class'], 'setHash');
|
||||
const spy = spyOn(this['class'], 'setHash');
|
||||
clickLine(13, {
|
||||
shiftKey: true,
|
||||
});
|
||||
|
@ -146,27 +137,25 @@ describe('LineHighlighter', function() {
|
|||
|
||||
describe('with existing single-line highlight', function() {
|
||||
it('uses existing line as last line when target is lesser', function() {
|
||||
var line;
|
||||
clickLine(20);
|
||||
clickLine(15, {
|
||||
shiftKey: true,
|
||||
});
|
||||
|
||||
expect($(`.${this.css}`).length).toBe(6);
|
||||
for (line = 15; line <= 20; line += 1) {
|
||||
for (let line = 15; line <= 20; line += 1) {
|
||||
expect($(`#LC${line}`)).toHaveClass(this.css);
|
||||
}
|
||||
});
|
||||
|
||||
it('uses existing line as first line when target is greater', function() {
|
||||
var line;
|
||||
clickLine(5);
|
||||
clickLine(10, {
|
||||
shiftKey: true,
|
||||
});
|
||||
|
||||
expect($(`.${this.css}`).length).toBe(6);
|
||||
for (line = 5; line <= 10; line += 1) {
|
||||
for (let line = 5; line <= 10; line += 1) {
|
||||
expect($(`#LC${line}`)).toHaveClass(this.css);
|
||||
}
|
||||
});
|
||||
|
@ -183,25 +172,23 @@ describe('LineHighlighter', function() {
|
|||
});
|
||||
|
||||
it('uses target as first line when it is less than existing first line', function() {
|
||||
var line;
|
||||
clickLine(5, {
|
||||
shiftKey: true,
|
||||
});
|
||||
|
||||
expect($(`.${this.css}`).length).toBe(6);
|
||||
for (line = 5; line <= 10; line += 1) {
|
||||
for (let line = 5; line <= 10; line += 1) {
|
||||
expect($(`#LC${line}`)).toHaveClass(this.css);
|
||||
}
|
||||
});
|
||||
|
||||
it('uses target as last line when it is greater than existing first line', function() {
|
||||
var line;
|
||||
clickLine(15, {
|
||||
shiftKey: true,
|
||||
});
|
||||
|
||||
expect($(`.${this.css}`).length).toBe(6);
|
||||
for (line = 10; line <= 15; line += 1) {
|
||||
for (let line = 10; line <= 15; line += 1) {
|
||||
expect($(`#LC${line}`)).toHaveClass(this.css);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -3,6 +3,8 @@ import GraphGroup from '~/monitoring/components/graph_group.vue';
|
|||
|
||||
describe('Graph group component', () => {
|
||||
let graphGroup;
|
||||
const findPrometheusGroup = () => graphGroup.find('.prometheus-graph-group');
|
||||
const findPrometheusPanel = () => graphGroup.find('.prometheus-panel');
|
||||
|
||||
afterEach(() => {
|
||||
graphGroup.destroy();
|
||||
|
@ -40,8 +42,32 @@ describe('Graph group component', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should not contain a prometheus-graph-group container when showPanels is false', () => {
|
||||
expect(graphGroup.vm.$el.querySelector('.prometheus-graph-group')).toBe(null);
|
||||
it('should not contain a prometheus-panel container when showPanels is false', () => {
|
||||
expect(findPrometheusPanel().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('When collapseGroup prop is updated', () => {
|
||||
beforeEach(() => {
|
||||
graphGroup = shallowMount(GraphGroup, {
|
||||
propsData: {
|
||||
name: 'panel',
|
||||
collapseGroup: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('previously collapsed group should respond to the prop change', done => {
|
||||
expect(findPrometheusGroup().exists()).toBe(false);
|
||||
|
||||
graphGroup.setProps({
|
||||
collapseGroup: true,
|
||||
});
|
||||
|
||||
graphGroup.vm.$nextTick(() => {
|
||||
expect(findPrometheusGroup().exists()).toBe(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::AttributesAtRefParser, :seed_helper do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::AttributesParser, :seed_helper do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe Gitlab::Git::Blame, :seed_helper do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe Gitlab::Git::Blob, :seed_helper do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe Gitlab::Git::Branch, :seed_helper do
|
||||
|
@ -77,7 +79,7 @@ describe Gitlab::Git::Branch, :seed_helper do
|
|||
tree = parents.first.tree
|
||||
|
||||
{
|
||||
message: 'commit message',
|
||||
message: +'commit message',
|
||||
author: committer,
|
||||
committer: committer,
|
||||
tree: tree,
|
||||
|
@ -126,7 +128,7 @@ describe Gitlab::Git::Branch, :seed_helper do
|
|||
it { expect(repository.branches.size).to eq(SeedRepo::Repo::BRANCHES.size) }
|
||||
|
||||
def create_commit
|
||||
params[:message].delete!("\r")
|
||||
params[:message].delete!(+"\r")
|
||||
Rugged::Commit.create(rugged, params.merge(committer: committer.merge(time: Time.now)))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::BundleFile do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe Gitlab::Git::Commit, :seed_helper do
|
||||
|
@ -64,8 +66,8 @@ describe Gitlab::Git::Commit, :seed_helper do
|
|||
end
|
||||
|
||||
describe "Commit info from gitaly commit" do
|
||||
let(:subject) { "My commit".force_encoding('ASCII-8BIT') }
|
||||
let(:body) { subject + "My body".force_encoding('ASCII-8BIT') }
|
||||
let(:subject) { (+"My commit").force_encoding('ASCII-8BIT') }
|
||||
let(:body) { subject + (+"My body").force_encoding('ASCII-8BIT') }
|
||||
let(:body_size) { body.length }
|
||||
let(:gitaly_commit) { build(:gitaly_commit, subject: subject, body: body, body_size: body_size) }
|
||||
let(:id) { gitaly_commit.id }
|
||||
|
@ -85,7 +87,7 @@ describe Gitlab::Git::Commit, :seed_helper do
|
|||
it { expect(commit.parent_ids).to eq(gitaly_commit.parent_ids) }
|
||||
|
||||
context 'body_size != body.size' do
|
||||
let(:body) { "".force_encoding('ASCII-8BIT') }
|
||||
let(:body) { (+"").force_encoding('ASCII-8BIT') }
|
||||
|
||||
context 'zero body_size' do
|
||||
it { expect(commit.safe_message).to eq(subject) }
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe Gitlab::Git::Compare, :seed_helper do
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
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)) }
|
||||
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
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::Conflict::Parser do
|
||||
|
@ -208,7 +210,7 @@ CONFLICT
|
|||
# these strings.
|
||||
context 'when the file contains UTF-8 characters' do
|
||||
it 'does not raise' do
|
||||
expect { parse_text("Espa\xC3\xB1a".force_encoding(Encoding::ASCII_8BIT)) }
|
||||
expect { parse_text((+"Espa\xC3\xB1a").force_encoding(Encoding::ASCII_8BIT)) }
|
||||
.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::DiffCollection, :seed_helper do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe Gitlab::Git::Diff, :seed_helper do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::GitmodulesParser do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::HookEnv do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::LfsChanges do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::LfsPointerFile do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::PreReceiveError do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::Push do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::RawDiffChange do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::RemoteMirror do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::RemoteRepository, :seed_helper do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::RepositoryCleaner do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe Gitlab::Git::Repository, :seed_helper do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe Gitlab::Git::Tag, :seed_helper do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe Gitlab::Git::Tree, :seed_helper do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::User do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::Util do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::Wiki do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::WrapsGitalyErrors do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Gpg::Commit do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Gpg::InvalidGpgSignatureUpdater do
|
||||
|
|
|
@ -990,7 +990,8 @@ describe Notify do
|
|||
end
|
||||
|
||||
context 'when a comment on an existing discussion' do
|
||||
let!(:second_note) { create(model, author: note_author, noteable: nil, in_reply_to: note) }
|
||||
let(:first_note) { create_note }
|
||||
let(:note) { create(model, author: note_author, noteable: nil, in_reply_to: first_note) }
|
||||
|
||||
it 'contains an introduction' do
|
||||
is_expected.to have_body_text 'commented on a'
|
||||
|
@ -1000,7 +1001,11 @@ describe Notify do
|
|||
|
||||
describe 'on a commit' do
|
||||
let(:commit) { project.commit }
|
||||
let(:note) { create(:discussion_note_on_commit, commit_id: commit.id, project: project, author: note_author) }
|
||||
let(:note) { create_note }
|
||||
|
||||
def create_note
|
||||
create(:discussion_note_on_commit, commit_id: commit.id, project: project, author: note_author)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(note).to receive(:noteable).and_return(commit)
|
||||
|
@ -1027,9 +1032,13 @@ describe Notify do
|
|||
end
|
||||
|
||||
describe 'on a merge request' do
|
||||
let(:note) { create(:discussion_note_on_merge_request, noteable: merge_request, project: project, author: note_author) }
|
||||
let(:note) { create_note }
|
||||
let(:note_on_merge_request_path) { project_merge_request_path(project, merge_request, anchor: "note_#{note.id}") }
|
||||
|
||||
def create_note
|
||||
create(:discussion_note_on_merge_request, noteable: merge_request, project: project, author: note_author)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(note).to receive(:noteable).and_return(merge_request)
|
||||
end
|
||||
|
@ -1055,9 +1064,13 @@ describe Notify do
|
|||
end
|
||||
|
||||
describe 'on an issue' do
|
||||
let(:note) { create(:discussion_note_on_issue, noteable: issue, project: project, author: note_author) }
|
||||
let(:note) { create_note }
|
||||
let(:note_on_issue_path) { project_issue_path(project, issue, anchor: "note_#{note.id}") }
|
||||
|
||||
def create_note
|
||||
create(:discussion_note_on_issue, noteable: issue, project: project, author: note_author)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(note).to receive(:noteable).and_return(issue)
|
||||
end
|
||||
|
@ -1134,7 +1147,8 @@ describe Notify do
|
|||
end
|
||||
|
||||
context 'when a comment on an existing discussion' do
|
||||
let!(:second_note) { create(model, author: note_author, noteable: nil, in_reply_to: note) }
|
||||
let(:first_note) { create(model) }
|
||||
let(:note) { create(model, author: note_author, noteable: nil, in_reply_to: first_note) }
|
||||
|
||||
it 'contains an introduction' do
|
||||
is_expected.to have_body_text 'commented on a discussion on'
|
||||
|
|
|
@ -456,6 +456,19 @@ describe Note do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#start_of_discussion?' do
|
||||
let_it_be(:note) { create(:discussion_note_on_merge_request) }
|
||||
let_it_be(:reply) { create(:discussion_note_on_merge_request, in_reply_to: note) }
|
||||
|
||||
it 'returns true when note is the start of a discussion' do
|
||||
expect(note).to be_start_of_discussion
|
||||
end
|
||||
|
||||
it 'returns false when note is a reply' do
|
||||
expect(reply).not_to be_start_of_discussion
|
||||
end
|
||||
end
|
||||
|
||||
describe '.find_discussion' do
|
||||
let!(:note) { create(:discussion_note_on_merge_request) }
|
||||
let!(:note2) { create(:discussion_note_on_merge_request, in_reply_to: note) }
|
||||
|
|
Loading…
Reference in New Issue