Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
1da3754b25
commit
8c726c7487
6 changed files with 225 additions and 163 deletions
|
@ -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"]
|
||||
|
|
214
spec/frontend/ide/components/merge_requests/list_spec.js
Normal file
214
spec/frontend/ide/components/merge_requests/list_spec.js
Normal file
|
@ -0,0 +1,214 @@
|
|||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,159 +0,0 @@
|
|||
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);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue