Skip to content
Snippets Groups Projects
Commit c27acb1d authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 18084543
No related branches found
No related tags found
No related merge requests found
import { mount } from '@vue/test-utils';
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
import { defaultTimeWindows } from '~/vue_shared/components/date_time_picker/date_time_picker_lib';
import {
defaultTimeRanges,
defaultTimeRange,
} from '~/vue_shared/components/date_time_picker/date_time_picker_lib';
 
const timeWindowsCount = Object.entries(defaultTimeWindows).length;
const start = '2019-10-10T07:00:00.000Z';
const end = '2019-10-13T07:00:00.000Z';
const selectedTimeWindowText = `3 days`;
const optionsCount = defaultTimeRanges.length;
 
describe('DateTimePicker', () => {
let dateTimePicker;
Loading
Loading
@@ -15,19 +15,10 @@ describe('DateTimePicker', () => {
const applyButtonElement = () => dateTimePicker.find('button.btn-success').element;
const findQuickRangeItems = () => dateTimePicker.findAll('.dropdown-item');
const cancelButtonElement = () => dateTimePicker.find('button.btn-secondary').element;
const fillInputAndBlur = (input, val) => {
dateTimePicker.find(input).setValue(val);
return dateTimePicker.vm.$nextTick().then(() => {
dateTimePicker.find(input).trigger('blur');
return dateTimePicker.vm.$nextTick();
});
};
 
const createComponent = props => {
dateTimePicker = mount(DateTimePicker, {
propsData: {
start,
end,
...props,
},
});
Loading
Loading
@@ -40,7 +31,7 @@ describe('DateTimePicker', () => {
it('renders dropdown toggle button with selected text', done => {
createComponent();
dateTimePicker.vm.$nextTick(() => {
expect(dropdownToggle().text()).toBe(selectedTimeWindowText);
expect(dropdownToggle().text()).toBe(defaultTimeRange.label);
done();
});
});
Loading
Loading
@@ -54,8 +45,10 @@ describe('DateTimePicker', () => {
 
it('renders inputs with h/m/s truncated if its all 0s', done => {
createComponent({
start: '2019-10-10T00:00:00.000Z',
end: '2019-10-14T00:10:00.000Z',
value: {
start: '2019-10-10T00:00:00.000Z',
end: '2019-10-14T00:10:00.000Z',
},
});
dateTimePicker.vm.$nextTick(() => {
expect(dateTimePicker.find('#custom-time-from').element.value).toBe('2019-10-10');
Loading
Loading
@@ -64,22 +57,21 @@ describe('DateTimePicker', () => {
});
});
 
it(`renders dropdown with ${timeWindowsCount} (default) items in quick range`, done => {
it(`renders dropdown with ${optionsCount} (default) items in quick range`, done => {
createComponent();
dropdownToggle().trigger('click');
dateTimePicker.vm.$nextTick(() => {
expect(findQuickRangeItems().length).toBe(timeWindowsCount);
expect(findQuickRangeItems().length).toBe(optionsCount);
done();
});
});
 
it(`renders dropdown with correct quick range item selected`, done => {
it('renders dropdown with a default quick range item selected', done => {
createComponent();
dropdownToggle().trigger('click');
dateTimePicker.vm.$nextTick(() => {
expect(dateTimePicker.find('.dropdown-item.active').text()).toBe(selectedTimeWindowText);
expect(dateTimePicker.find('.dropdown-item.active svg').isVisible()).toBe(true);
expect(dateTimePicker.find('.dropdown-item.active').exists()).toBe(true);
expect(dateTimePicker.find('.dropdown-item.active').text()).toBe(defaultTimeRange.label);
done();
});
});
Loading
Loading
@@ -92,99 +84,142 @@ describe('DateTimePicker', () => {
expect(applyButtonElement().getAttribute('disabled')).toBe('disabled');
});
 
it('displays inline error message if custom time range inputs are invalid', done => {
createComponent();
fillInputAndBlur('#custom-time-from', '2019-10-01abc')
.then(() => fillInputAndBlur('#custom-time-to', '2019-10-10abc'))
.then(() => {
expect(dateTimePicker.findAll('.invalid-feedback').length).toBe(2);
done();
})
.catch(done);
});
describe('user input', () => {
const fillInputAndBlur = (input, val) => {
dateTimePicker.find(input).setValue(val);
return dateTimePicker.vm.$nextTick().then(() => {
dateTimePicker.find(input).trigger('blur');
return dateTimePicker.vm.$nextTick();
});
};
 
it('keeps apply button disabled with invalid custom time range inputs', done => {
createComponent();
fillInputAndBlur('#custom-time-from', '2019-10-01abc')
.then(() => fillInputAndBlur('#custom-time-to', '2019-09-19'))
.then(() => {
expect(applyButtonElement().getAttribute('disabled')).toBe('disabled');
done();
})
.catch(done);
});
beforeEach(done => {
createComponent();
dateTimePicker.vm.$nextTick(done);
});
 
it('enables apply button with valid custom time range inputs', done => {
createComponent();
fillInputAndBlur('#custom-time-from', '2019-10-01')
.then(() => fillInputAndBlur('#custom-time-to', '2019-10-19'))
.then(() => {
expect(applyButtonElement().getAttribute('disabled')).toBeNull();
done();
})
.catch(done.fail);
});
it('displays inline error message if custom time range inputs are invalid', done => {
fillInputAndBlur('#custom-time-from', '2019-10-01abc')
.then(() => fillInputAndBlur('#custom-time-to', '2019-10-10abc'))
.then(() => {
expect(dateTimePicker.findAll('.invalid-feedback').length).toBe(2);
done();
})
.catch(done);
});
 
it('emits dates in an object when apply is clicked', done => {
createComponent();
fillInputAndBlur('#custom-time-from', '2019-10-01')
.then(() => fillInputAndBlur('#custom-time-to', '2019-10-19'))
.then(() => {
applyButtonElement().click();
expect(dateTimePicker.emitted().apply).toHaveLength(1);
expect(dateTimePicker.emitted().apply[0]).toEqual([
{
end: '2019-10-19T00:00:00Z',
start: '2019-10-01T00:00:00Z',
},
]);
done();
})
.catch(done.fail);
});
it('keeps apply button disabled with invalid custom time range inputs', done => {
fillInputAndBlur('#custom-time-from', '2019-10-01abc')
.then(() => fillInputAndBlur('#custom-time-to', '2019-09-19'))
.then(() => {
expect(applyButtonElement().getAttribute('disabled')).toBe('disabled');
done();
})
.catch(done);
});
 
it('hides the popover with cancel button', done => {
createComponent();
dropdownToggle().trigger('click');
it('enables apply button with valid custom time range inputs', done => {
fillInputAndBlur('#custom-time-from', '2019-10-01')
.then(() => fillInputAndBlur('#custom-time-to', '2019-10-19'))
.then(() => {
expect(applyButtonElement().getAttribute('disabled')).toBeNull();
done();
})
.catch(done.fail);
});
 
dateTimePicker.vm.$nextTick(() => {
cancelButtonElement().click();
it('emits dates in an object when apply is clicked', done => {
fillInputAndBlur('#custom-time-from', '2019-10-01')
.then(() => fillInputAndBlur('#custom-time-to', '2019-10-19'))
.then(() => {
applyButtonElement().click();
expect(dateTimePicker.emitted().input).toHaveLength(1);
expect(dateTimePicker.emitted().input[0]).toEqual([
{
end: '2019-10-19T00:00:00Z',
start: '2019-10-01T00:00:00Z',
},
]);
done();
})
.catch(done.fail);
});
it('unchecks quick range when text is input is clicked', done => {
const findActiveItems = () => findQuickRangeItems().filter(w => w.is('.active'));
expect(findActiveItems().length).toBe(1);
fillInputAndBlur('#custom-time-from', '2019-10-01')
.then(() => {
expect(findActiveItems().length).toBe(0);
done();
})
.catch(done.fail);
});
it('emits dates in an object when a is clicked', () => {
findQuickRangeItems()
.at(3) // any item
.trigger('click');
expect(dateTimePicker.emitted().input).toHaveLength(1);
expect(dateTimePicker.emitted().input[0][0]).toMatchObject({
duration: {
seconds: expect.any(Number),
},
});
});
it('hides the popover with cancel button', done => {
dropdownToggle().trigger('click');
 
dateTimePicker.vm.$nextTick(() => {
expect(dropdownMenu().classes('show')).toBe(false);
done();
cancelButtonElement().click();
dateTimePicker.vm.$nextTick(() => {
expect(dropdownMenu().classes('show')).toBe(false);
done();
});
});
});
});
 
describe('when using non-default time windows', () => {
const otherTimeWindows = {
oneMinute: {
const MOCK_NOW = Date.UTC(2020, 0, 23, 20);
const otherTimeRanges = [
{
label: '1 minute',
seconds: 60,
duration: { seconds: 60 },
},
twoMinutes: {
{
label: '2 minutes',
seconds: 60 * 2,
duration: { seconds: 60 * 2 },
default: true,
},
fiveMinutes: {
{
label: '5 minutes',
seconds: 60 * 5,
duration: { seconds: 60 * 5 },
},
};
];
beforeEach(() => {
jest.spyOn(Date, 'now').mockImplementation(() => MOCK_NOW);
});
 
it('renders dropdown with a label in the quick range', done => {
createComponent({
// 2 minutes range
start: '2020-01-21T15:00:00.000Z',
end: '2020-01-21T15:02:00.000Z',
timeWindows: otherTimeWindows,
value: {
duration: { seconds: 60 * 5 },
},
options: otherTimeRanges,
});
dropdownToggle().trigger('click');
dateTimePicker.vm.$nextTick(() => {
expect(dropdownToggle().text()).toBe('2 minutes');
expect(dropdownToggle().text()).toBe('5 minutes');
 
done();
});
Loading
Loading
@@ -192,16 +227,16 @@ describe('DateTimePicker', () => {
 
it('renders dropdown with quick range items', done => {
createComponent({
// 2 minutes range
start: '2020-01-21T15:00:00.000Z',
end: '2020-01-21T15:02:00.000Z',
timeWindows: otherTimeWindows,
value: {
duration: { seconds: 60 * 2 },
},
options: otherTimeRanges,
});
dropdownToggle().trigger('click');
dateTimePicker.vm.$nextTick(() => {
const items = findQuickRangeItems();
 
expect(items.length).toBe(Object.keys(otherTimeWindows).length);
expect(items.length).toBe(Object.keys(otherTimeRanges).length);
expect(items.at(0).text()).toBe('1 minute');
expect(items.at(0).is('.active')).toBe(false);
 
Loading
Loading
@@ -217,14 +252,13 @@ describe('DateTimePicker', () => {
 
it('renders dropdown with a label not in the quick range', done => {
createComponent({
// 10 minutes range
start: '2020-01-21T15:00:00.000Z',
end: '2020-01-21T15:10:00.000Z',
timeWindows: otherTimeWindows,
value: {
duration: { seconds: 60 * 4 },
},
});
dropdownToggle().trigger('click');
dateTimePicker.vm.$nextTick(() => {
expect(dropdownToggle().text()).toBe('2020-01-21 15:00:00 to 2020-01-21 15:10:00');
expect(dropdownToggle().text()).toBe('2020-01-23 19:56:00 to 2020-01-23 20:00:00');
 
done();
});
Loading
Loading
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::BackgroundMigration::UpdateExistingSubgroupToMatchVisibilityLevelOfParent, :migration, schema: 2020_01_10_121314 do
include MigrationHelpers::NamespacesHelpers
context 'private visibility level' do
it 'updates the project visibility' do
parent = create_namespace('parent', Gitlab::VisibilityLevel::PRIVATE)
child = create_namespace('child', Gitlab::VisibilityLevel::PUBLIC, parent_id: parent.id)
expect { subject.perform([parent.id], Gitlab::VisibilityLevel::PRIVATE) }.to change { child.reload.visibility_level }.to(Gitlab::VisibilityLevel::PRIVATE)
end
it 'updates sub-sub groups' do
parent = create_namespace('parent', Gitlab::VisibilityLevel::PRIVATE)
middle_group = create_namespace('middle', Gitlab::VisibilityLevel::PRIVATE, parent_id: parent.id)
child = create_namespace('child', Gitlab::VisibilityLevel::PUBLIC, parent_id: middle_group.id)
subject.perform([parent.id, middle_group.id], Gitlab::VisibilityLevel::PRIVATE)
expect(child.reload.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
end
it 'updates all sub groups' do
parent = create_namespace('parent', Gitlab::VisibilityLevel::PRIVATE)
middle_group = create_namespace('middle', Gitlab::VisibilityLevel::PUBLIC, parent_id: parent.id)
child = create_namespace('child', Gitlab::VisibilityLevel::PUBLIC, parent_id: middle_group.id)
subject.perform([parent.id], Gitlab::VisibilityLevel::PRIVATE)
expect(child.reload.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
expect(middle_group.reload.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
end
end
context 'internal visibility level' do
it 'updates the project visibility' do
parent = create_namespace('parent', Gitlab::VisibilityLevel::INTERNAL)
child = create_namespace('child', Gitlab::VisibilityLevel::PUBLIC, parent_id: parent.id)
expect { subject.perform([parent.id], Gitlab::VisibilityLevel::INTERNAL) }.to change { child.reload.visibility_level }.to(Gitlab::VisibilityLevel::INTERNAL)
end
end
end
Loading
Loading
@@ -128,4 +128,23 @@ describe Gitlab::Database::WithLockRetries do
end
end
end
context 'casting durations correctly' do
let(:timing_configuration) { [[0.015.seconds, 0.025.seconds], [0.015.seconds, 0.025.seconds]] } # 15ms, 25ms
it 'executes `SET LOCAL lock_timeout` using the configured timeout value in milliseconds' do
expect(ActiveRecord::Base.connection).to receive(:execute).with("SAVEPOINT active_record_1").and_call_original
expect(ActiveRecord::Base.connection).to receive(:execute).with("SET LOCAL lock_timeout TO '15ms'").and_call_original
expect(ActiveRecord::Base.connection).to receive(:execute).with("RELEASE SAVEPOINT active_record_1").and_call_original
subject.run { }
end
it 'calls `sleep` after the first iteration fails, using the configured sleep time' do
expect(subject).to receive(:run_block_with_transaction).and_raise(ActiveRecord::LockWaitTimeout).twice
expect(subject).to receive(:sleep).with(0.025)
subject.run { }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20200110121314_schedule_update_existing_subgroup_to_match_visibility_level_of_parent.rb')
describe ScheduleUpdateExistingSubgroupToMatchVisibilityLevelOfParent, :migration, :sidekiq do
include MigrationHelpers::NamespacesHelpers
let(:migration_class) { described_class::MIGRATION }
let(:migration_name) { migration_class.to_s.demodulize }
context 'private visibility level' do
it 'correctly schedules background migrations' do
parent = create_namespace('parent', Gitlab::VisibilityLevel::PRIVATE)
create_namespace('child', Gitlab::VisibilityLevel::PUBLIC, parent_id: parent.id)
Sidekiq::Testing.fake! do
Timecop.freeze do
migrate!
expect(BackgroundMigrationWorker.jobs.size).to eq(1)
expect(migration_name).to be_scheduled_migration_with_multiple_args([parent.id], Gitlab::VisibilityLevel::PRIVATE)
end
end
end
it 'correctly schedules background migrations for groups and subgroups' do
parent = create_namespace('parent', Gitlab::VisibilityLevel::PRIVATE)
middle_group = create_namespace('middle_group', Gitlab::VisibilityLevel::PRIVATE, parent_id: parent.id)
create_namespace('middle_empty_group', Gitlab::VisibilityLevel::PRIVATE, parent_id: parent.id)
create_namespace('child', Gitlab::VisibilityLevel::PUBLIC, parent_id: middle_group.id)
Sidekiq::Testing.fake! do
Timecop.freeze do
migrate!
expect(BackgroundMigrationWorker.jobs.size).to eq(1)
expect(migration_name).to be_scheduled_migration_with_multiple_args([middle_group.id, parent.id], Gitlab::VisibilityLevel::PRIVATE)
end
end
end
end
context 'internal visibility level' do
it 'correctly schedules background migrations' do
parent = create_namespace('parent', Gitlab::VisibilityLevel::INTERNAL)
middle_group = create_namespace('child', Gitlab::VisibilityLevel::INTERNAL, parent_id: parent.id)
create_namespace('child', Gitlab::VisibilityLevel::PUBLIC, parent_id: middle_group.id)
Sidekiq::Testing.fake! do
Timecop.freeze do
migrate!
expect(BackgroundMigrationWorker.jobs.size).to eq(1)
expect(migration_name).to be_scheduled_migration_with_multiple_args([parent.id, middle_group.id], Gitlab::VisibilityLevel::INTERNAL)
end
end
end
end
context 'mixed visibility levels' do
it 'correctly schedules background migrations' do
parent1 = create_namespace('parent1', Gitlab::VisibilityLevel::INTERNAL)
create_namespace('child', Gitlab::VisibilityLevel::PUBLIC, parent_id: parent1.id)
parent2 = create_namespace('parent2', Gitlab::VisibilityLevel::PRIVATE)
middle_group = create_namespace('middle_group', Gitlab::VisibilityLevel::INTERNAL, parent_id: parent2.id)
create_namespace('child', Gitlab::VisibilityLevel::PUBLIC, parent_id: middle_group.id)
Sidekiq::Testing.fake! do
Timecop.freeze do
migrate!
expect(BackgroundMigrationWorker.jobs.size).to eq(2)
expect(migration_name).to be_scheduled_migration_with_multiple_args([parent1.id, middle_group.id], Gitlab::VisibilityLevel::INTERNAL)
expect(migration_name).to be_scheduled_migration_with_multiple_args([parent2.id], Gitlab::VisibilityLevel::PRIVATE)
end
end
end
end
end
Loading
Loading
@@ -26,3 +26,26 @@ RSpec::Matchers.define :be_scheduled_migration do |*expected|
"Migration `#{migration}` with args `#{expected.inspect}` not scheduled!"
end
end
RSpec::Matchers.define :be_scheduled_migration_with_multiple_args do |*expected|
match do |migration|
BackgroundMigrationWorker.jobs.any? do |job|
args = job['args'].size == 1 ? [BackgroundMigrationWorker.jobs[0]['args'][0], []] : job['args']
args[0] == migration && compare_args(args, expected)
end
end
failure_message do |migration|
"Migration `#{migration}` with args `#{expected.inspect}` not scheduled!"
end
def compare_args(args, expected)
args[1].map.with_index do |arg, i|
arg.is_a?(Array) ? same_arrays?(arg, expected[i]) : arg == expected[i]
end.all?
end
def same_arrays?(arg, expected)
arg.sort == expected.sort
end
end
# frozen_string_literal: true
module MigrationHelpers
module NamespacesHelpers
def create_namespace(name, visibility, options = {})
table(:namespaces).create({
name: name,
path: name,
type: 'Group',
visibility_level: visibility
}.merge(options))
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment