Commit 8c726c74 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 1da3754b
......@@ -3,6 +3,7 @@
# Ignore all folders except qa/, config/initializers and the root of lib/ since
# the files we need to build the QA image are in these folders.
# Following are the files we need:
# - ./config/light_settings.rb
# - ./config/initializers/0_inject_enterprise_edition_module.rb
# - ./ee/app/models/license.rb
# - ./lib/gitlab.rb
......
......@@ -180,3 +180,5 @@ class Deployment < ApplicationRecord
self.created_at if success? && !read_attribute(:finished_at)
end
end
Deployment.prepend_if_ee('EE::Deployment')
......@@ -67,3 +67,5 @@ class Release < ApplicationRecord
end
end
end
Release.prepend_if_ee('EE::Release')
......@@ -49,13 +49,15 @@ RUN export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)" && \
WORKDIR /home/gitlab/qa
COPY ./qa/Gemfile* /home/gitlab/qa/
COPY ./config/light_settings.rb /home/gitlab/config/light_settings.rb
COPY ./config/initializers/0_inject_enterprise_edition_module.rb /home/gitlab/config/initializers/
# Copy VERSION to ensure the COPY succeeds to copy at least one file since ee/app/models/license.rb isn't present in CE
# Copy VERSION to ensure the COPY succeeds to copy at least one file since ee/app/models/license.rb isn't present in FOSS
# The [b] part makes ./ee/app/models/license.r[b] a pattern that is allowed to return no files (which is the case in FOSS)
COPY VERSION ./ee/app/models/license.r[b] /home/gitlab/ee/app/models/
COPY ./config/light_settings.rb /home/gitlab/config/
COPY ./lib/gitlab.rb /home/gitlab/lib/
COPY ./INSTALLATION_TYPE /home/gitlab/
COPY ./VERSION /home/gitlab/
RUN cd /home/gitlab/qa/ && bundle install
COPY ./INSTALLATION_TYPE ./VERSION /home/gitlab/
RUN cd /home/gitlab/qa/ && bundle install --jobs=$(nproc) --retry=3 --quiet
COPY ./qa /home/gitlab/qa
ENTRYPOINT ["bin/test"]
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import List from '~/ide/components/merge_requests/list.vue';
import Item from '~/ide/components/merge_requests/item.vue';
import TokenedInput from '~/ide/components/shared/tokened_input.vue';
import { GlLoadingIcon } from '@gitlab/ui';
import { mergeRequests as mergeRequestsMock } from '../../mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('IDE merge requests list', () => {
let wrapper;
let fetchMergeRequestsMock;
const findSearchTypeButtons = () => wrapper.findAll('button');
const findTokenedInput = () => wrapper.find(TokenedInput);
const createComponent = (state = {}) => {
const { mergeRequests = {}, ...restOfState } = state;
const fakeStore = new Vuex.Store({
state: {
currentMergeRequestId: '1',
currentProjectId: 'project/master',
...restOfState,
},
modules: {
mergeRequests: {
namespaced: true,
state: {
isLoading: false,
mergeRequests: [],
...mergeRequests,
},
actions: {
fetchMergeRequests: fetchMergeRequestsMock,
},
},
},
});
wrapper = shallowMount(List, {
store: fakeStore,
localVue,
sync: false,
});
};
beforeEach(() => {
fetchMergeRequestsMock = jest.fn();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('calls fetch on mounted', () => {
createComponent();
expect(fetchMergeRequestsMock).toHaveBeenCalledWith(
expect.any(Object),
{
search: '',
type: '',
},
undefined,
);
});
it('renders loading icon when merge request is loading', () => {
createComponent({ mergeRequests: { isLoading: true } });
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
});
it('renders no search results text when search is not empty', () => {
createComponent();
findTokenedInput().vm.$emit('input', 'something');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.text()).toContain('No merge requests found');
});
});
it('clicking on search type, sets currentSearchType and loads merge requests', () => {
createComponent();
findTokenedInput().vm.$emit('focus');
return wrapper.vm
.$nextTick()
.then(() => {
findSearchTypeButtons()
.at(0)
.trigger('click');
return wrapper.vm.$nextTick();
})
.then(() => {
const searchType = wrapper.vm.$options.searchTypes[0];
expect(findTokenedInput().props('tokens')).toEqual([searchType]);
expect(fetchMergeRequestsMock).toHaveBeenCalledWith(
expect.any(Object),
{
type: searchType.type,
search: '',
},
undefined,
);
});
});
describe('with merge requests', () => {
let defaultStateWithMergeRequests;
beforeAll(() => {
defaultStateWithMergeRequests = {
mergeRequests: {
isLoading: false,
mergeRequests: [
{ ...mergeRequestsMock[0], projectPathWithNamespace: 'gitlab-org/gitlab-foss' },
],
},
};
});
it('renders list', () => {
createComponent(defaultStateWithMergeRequests);
expect(wrapper.findAll(Item).length).toBe(1);
expect(wrapper.find(Item).props('item')).toBe(
defaultStateWithMergeRequests.mergeRequests.mergeRequests[0],
);
});
describe('when searching merge requests', () => {
it('calls `loadMergeRequests` on input in search field', () => {
createComponent(defaultStateWithMergeRequests);
const input = findTokenedInput();
input.vm.$emit('input', 'something');
fetchMergeRequestsMock.mockClear();
jest.runAllTimers();
return wrapper.vm.$nextTick().then(() => {
expect(fetchMergeRequestsMock).toHaveBeenCalledWith(
expect.any(Object),
{
search: 'something',
type: '',
},
undefined,
);
});
});
});
});
describe('on search focus', () => {
let input;
beforeEach(() => {
createComponent();
input = findTokenedInput();
});
describe('without search value', () => {
beforeEach(() => {
input.vm.$emit('focus');
return wrapper.vm.$nextTick();
});
it('shows search types', () => {
const buttons = findSearchTypeButtons();
expect(buttons.wrappers.map(x => x.text().trim())).toEqual(
wrapper.vm.$options.searchTypes.map(x => x.label),
);
});
it('hides search types when search changes', () => {
input.vm.$emit('input', 'something');
return wrapper.vm.$nextTick().then(() => {
expect(findSearchTypeButtons().exists()).toBe(false);
});
});
describe('with search type', () => {
beforeEach(() => {
findSearchTypeButtons()
.at(0)
.trigger('click');
return wrapper.vm
.$nextTick()
.then(() => input.vm.$emit('focus'))
.then(() => wrapper.vm.$nextTick());
});
it('does not show search types', () => {
expect(findSearchTypeButtons().exists()).toBe(false);
});
});
});
describe('with search value', () => {
beforeEach(() => {
input.vm.$emit('input', 'something');
input.vm.$emit('focus');
return wrapper.vm.$nextTick();
});
it('does not show search types', () => {
expect(findSearchTypeButtons().exists()).toBe(false);
});
});
});
});
import Vue from 'vue';
import store from '~/ide/stores';
import List from '~/ide/components/merge_requests/list.vue';
import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
import { mergeRequests } from '../../mock_data';
import { resetStore } from '../../helpers';
describe('IDE merge requests list', () => {
const Component = Vue.extend(List);
let vm;
beforeEach(() => {
vm = createComponentWithStore(Component, store, {});
spyOn(vm, 'fetchMergeRequests');
vm.$mount();
});
afterEach(() => {
vm.$destroy();
resetStore(vm.$store);
});
it('calls fetch on mounted', () => {
expect(vm.fetchMergeRequests).toHaveBeenCalledWith({
search: '',
type: '',
});
});
it('renders loading icon', done => {
vm.$store.state.mergeRequests.isLoading = true;
vm.$nextTick(() => {
expect(vm.$el.querySelector('.loading-container')).not.toBe(null);
done();
});
});
it('renders no search results text when search is not empty', done => {
vm.search = 'testing';
vm.$nextTick(() => {
expect(vm.$el.textContent).toContain('No merge requests found');
done();
});
});
it('clicking on search type, sets currentSearchType and loads merge requests', done => {
vm.onSearchFocus();
vm.$nextTick()
.then(() => {
vm.$el.querySelector('li button').click();
return vm.$nextTick();
})
.then(() => {
expect(vm.currentSearchType).toEqual(vm.$options.searchTypes[0]);
expect(vm.fetchMergeRequests).toHaveBeenCalledWith({
type: vm.currentSearchType.type,
search: '',
});
})
.then(done)
.catch(done.fail);
});
describe('with merge requests', () => {
beforeEach(done => {
vm.$store.state.mergeRequests.mergeRequests.push({
...mergeRequests[0],
projectPathWithNamespace: 'gitlab-org/gitlab-ce',
});
vm.$nextTick(done);
});
it('renders list', () => {
expect(vm.$el.querySelectorAll('li').length).toBe(1);
expect(vm.$el.querySelector('li').textContent).toContain(mergeRequests[0].title);
});
});
describe('searchMergeRequests', () => {
beforeEach(() => {
spyOn(vm, 'loadMergeRequests');
jasmine.clock().install();
});
afterEach(() => {
jasmine.clock().uninstall();
});
it('calls loadMergeRequests on input in search field', () => {
const event = new Event('input');
vm.$el.querySelector('input').dispatchEvent(event);
jasmine.clock().tick(300);
expect(vm.loadMergeRequests).toHaveBeenCalled();
});
});
describe('onSearchFocus', () => {
it('shows search types', done => {
vm.$el.querySelector('input').dispatchEvent(new Event('focus'));
expect(vm.hasSearchFocus).toBe(true);
expect(vm.showSearchTypes).toBe(true);
vm.$nextTick()
.then(() => {
const expectedSearchTypes = vm.$options.searchTypes.map(x => x.label);
const renderedSearchTypes = Array.from(vm.$el.querySelectorAll('li')).map(x =>
x.textContent.trim(),
);
expect(renderedSearchTypes).toEqual(expectedSearchTypes);
})
.then(done)
.catch(done.fail);
});
it('does not show search types, if already has search value', () => {
vm.search = 'lorem ipsum';
vm.$el.querySelector('input').dispatchEvent(new Event('focus'));
expect(vm.hasSearchFocus).toBe(true);
expect(vm.showSearchTypes).toBe(false);
});
it('does not show search types, if already has a search type', () => {
vm.currentSearchType = {};
vm.$el.querySelector('input').dispatchEvent(new Event('focus'));
expect(vm.hasSearchFocus).toBe(true);
expect(vm.showSearchTypes).toBe(false);
});
it('resets hasSearchFocus when search changes', done => {
vm.hasSearchFocus = true;
vm.search = 'something else';
vm.$nextTick()
.then(() => {
expect(vm.hasSearchFocus).toBe(false);
})
.then(done)
.catch(done.fail);
});
});
});
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