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

Add latest changes from gitlab-org/gitlab@master

parent bcc77054
No related branches found
No related tags found
No related merge requests found
Showing
with 521 additions and 551 deletions
Loading
Loading
@@ -154,12 +154,12 @@ describe Projects::ServicesController do
end
end
 
context 'when activating Jira service from instance level service' do
context 'when activating Jira service from a template' do
let(:service) do
create(:jira_service, project: project, instance: true)
create(:jira_service, project: project, template: true)
end
 
it 'activate Jira service from instance level service' do
it 'activate Jira service from template' do
expect(flash[:notice]).to eq 'Jira activated.'
end
end
Loading
Loading
Loading
Loading
@@ -380,5 +380,9 @@ x6zG6WoibsbsJMj70nwseUnPTBQNDP+j61RJjC/r
scope { :instance }
usage { :serverless }
end
trait :with_project do
association :project
end
end
end
Loading
Loading
@@ -153,8 +153,8 @@ describe Projects::Serverless::FunctionsFinder do
*knative_services_finder.cache_args)
 
result = finder.service(cluster.environment_scope, cluster.project.name)
expect(result).not_to be_empty
expect(result["metadata"]["name"]).to be_eql(cluster.project.name)
expect(result).to be_present
expect(result.name).to be_eql(cluster.project.name)
end
 
it 'has metrics', :use_clean_rails_memory_store_caching do
Loading
Loading
Loading
Loading
@@ -2736,7 +2736,7 @@ Service
when repository is empty
test runs execute
Template
.build_from_instance
.build_from_template
when template is invalid
sets service template to inactive when template is invalid
for pushover service
Loading
Loading
import { mount, shallowMount } from '@vue/test-utils';
import axios from '~/lib/utils/axios_utils';
import MockAdapter from 'axios-mock-adapter';
import Container from '~/environments/components/container.vue';
import EmptyState from '~/environments/components/empty_state.vue';
import EnvironmentsApp from '~/environments/components/environments_app.vue';
import { environment, folder } from './mock_data';
describe('Environment', () => {
let mock;
let wrapper;
const mockData = {
endpoint: 'environments.json',
canCreateEnvironment: true,
canReadEnvironment: true,
newEnvironmentPath: 'environments/new',
helpPagePath: 'help',
canaryDeploymentFeatureId: 'canary_deployment',
showCanaryDeploymentCallout: true,
userCalloutsPath: '/callouts',
lockPromotionSvgPath: '/assets/illustrations/lock-promotion.svg',
helpCanaryDeploymentsPath: 'help/canary-deployments',
};
const mockRequest = (response, body) => {
mock.onGet(mockData.endpoint).reply(response, body, {
'X-nExt-pAge': '2',
'x-page': '1',
'X-Per-Page': '1',
'X-Prev-Page': '',
'X-TOTAL': '37',
'X-Total-Pages': '2',
});
};
const createWrapper = (shallow = false) => {
const fn = shallow ? shallowMount : mount;
wrapper = fn(EnvironmentsApp, { propsData: mockData });
return axios.waitForAll();
};
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
wrapper.destroy();
mock.restore();
});
describe('successful request', () => {
describe('without environments', () => {
beforeEach(() => {
mockRequest(200, { environments: [] });
return createWrapper(true);
});
it('should render the empty state', () => {
expect(wrapper.find(EmptyState).exists()).toBe(true);
});
describe('when it is possible to enable a review app', () => {
beforeEach(() => {
mockRequest(200, { environments: [], review_app: { can_setup_review_app: true } });
return createWrapper();
});
it('should render the enable review app button', () => {
expect(wrapper.find('.js-enable-review-app-button').text()).toContain(
'Enable review app',
);
});
});
});
describe('with paginated environments', () => {
const environmentList = [environment];
beforeEach(() => {
mockRequest(200, {
environments: environmentList,
stopped_count: 1,
available_count: 0,
});
return createWrapper();
});
it('should render a conatiner table with environments', () => {
const containerTable = wrapper.find(Container);
expect(containerTable.exists()).toBe(true);
expect(containerTable.props('environments').length).toEqual(environmentList.length);
expect(containerTable.find('.environment-name').text()).toEqual(environmentList[0].name);
});
describe('pagination', () => {
it('should render pagination', () => {
expect(wrapper.findAll('.gl-pagination li').length).toEqual(9);
});
it('should make an API request when page is clicked', () => {
jest.spyOn(wrapper.vm, 'updateContent').mockImplementation(() => {});
wrapper.find('.gl-pagination li:nth-child(3) .page-link').trigger('click');
expect(wrapper.vm.updateContent).toHaveBeenCalledWith({ scope: 'available', page: '2' });
});
it('should make an API request when using tabs', () => {
jest.spyOn(wrapper.vm, 'updateContent').mockImplementation(() => {});
wrapper.find('.js-environments-tab-stopped').trigger('click');
expect(wrapper.vm.updateContent).toHaveBeenCalledWith({ scope: 'stopped', page: '1' });
});
});
});
});
describe('unsuccessful request', () => {
beforeEach(() => {
mockRequest(500, {});
return createWrapper(true);
});
it('should render empty state', () => {
expect(wrapper.find(EmptyState).exists()).toBe(true);
});
});
describe('expandable folders', () => {
beforeEach(() => {
mockRequest(200, {
environments: [folder],
stopped_count: 1,
available_count: 0,
});
mock.onGet(environment.folder_path).reply(200, { environments: [environment] });
return createWrapper().then(() => {
// open folder
wrapper.find('.folder-name').trigger('click');
return axios.waitForAll();
});
});
it('should open a closed folder', () => {
expect(wrapper.find('.folder-icon.ic-chevron-right').exists()).toBe(false);
});
it('should close an opened folder', () => {
expect(wrapper.find('.folder-icon.ic-chevron-down').exists()).toBe(true);
// close folder
wrapper.find('.folder-name').trigger('click');
wrapper.vm.$nextTick(() => {
expect(wrapper.find('.folder-icon.ic-chevron-down').exists()).toBe(false);
});
});
it('should show children environments', () => {
expect(wrapper.findAll('.js-child-row').length).toEqual(1);
});
it('should show a button to show all environments', () => {
expect(wrapper.find('.text-center > a.btn').text()).toContain('Show all');
});
});
});
Loading
Loading
@@ -91,10 +91,10 @@ describe('Dashboard', () => {
});
 
describe('no data found', () => {
beforeEach(done => {
beforeEach(() => {
createShallowWrapper();
 
wrapper.vm.$nextTick(done);
return wrapper.vm.$nextTick();
});
 
it('shows the environment selector dropdown', () => {
Loading
Loading
@@ -118,20 +118,15 @@ describe('Dashboard', () => {
});
});
 
it('shows up a loading state', done => {
it('shows up a loading state', () => {
createShallowWrapper({ hasMetrics: true }, { methods: {} });
 
wrapper.vm
.$nextTick()
.then(() => {
expect(wrapper.vm.emptyState).toEqual('loading');
done();
})
.catch(done.fail);
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.vm.emptyState).toEqual('loading');
});
});
 
it('hides the group panels when showPanels is false', done => {
it('hides the group panels when showPanels is false', () => {
createMountedWrapper(
{ hasMetrics: true, showPanels: false },
{ stubs: ['graph-group', 'panel-type'] },
Loading
Loading
@@ -139,15 +134,10 @@ describe('Dashboard', () => {
 
setupComponentStore(wrapper);
 
wrapper.vm
.$nextTick()
.then(() => {
expect(wrapper.vm.showEmptyState).toEqual(false);
expect(wrapper.findAll('.prometheus-panel')).toHaveLength(0);
done();
})
.catch(done.fail);
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.vm.showEmptyState).toEqual(false);
expect(wrapper.findAll('.prometheus-panel')).toHaveLength(0);
});
});
 
it('fetches the metrics data with proper time window', () => {
Loading
Loading
@@ -171,43 +161,32 @@ describe('Dashboard', () => {
createMountedWrapper({ hasMetrics: true }, { stubs: ['graph-group', 'panel-type'] });
 
setupComponentStore(wrapper);
return wrapper.vm.$nextTick();
});
 
it('renders the environments dropdown with a number of environments', done => {
wrapper.vm
.$nextTick()
.then(() => {
expect(findAllEnvironmentsDropdownItems().length).toEqual(environmentData.length);
findAllEnvironmentsDropdownItems().wrappers.forEach((itemWrapper, index) => {
const anchorEl = itemWrapper.find('a');
if (anchorEl.exists() && environmentData[index].metrics_path) {
const href = anchorEl.attributes('href');
expect(href).toBe(environmentData[index].metrics_path);
}
});
it('renders the environments dropdown with a number of environments', () => {
expect(findAllEnvironmentsDropdownItems().length).toEqual(environmentData.length);
 
done();
})
.catch(done.fail);
findAllEnvironmentsDropdownItems().wrappers.forEach((itemWrapper, index) => {
const anchorEl = itemWrapper.find('a');
if (anchorEl.exists() && environmentData[index].metrics_path) {
const href = anchorEl.attributes('href');
expect(href).toBe(environmentData[index].metrics_path);
}
});
});
 
it('renders the environments dropdown with a single active element', done => {
wrapper.vm
.$nextTick()
.then(() => {
const activeItem = findAllEnvironmentsDropdownItems().wrappers.filter(itemWrapper =>
itemWrapper.find('.active').exists(),
);
it('renders the environments dropdown with a single active element', () => {
const activeItem = findAllEnvironmentsDropdownItems().wrappers.filter(itemWrapper =>
itemWrapper.find('.active').exists(),
);
 
expect(activeItem.length).toBe(1);
done();
})
.catch(done.fail);
expect(activeItem.length).toBe(1);
});
});
 
it('hides the environments dropdown list when there is no environments', done => {
it('hides the environments dropdown list when there is no environments', () => {
createMountedWrapper({ hasMetrics: true }, { stubs: ['graph-group', 'panel-type'] });
 
wrapper.vm.$store.commit(
Loading
Loading
@@ -219,35 +198,27 @@ describe('Dashboard', () => {
mockedQueryResultPayload,
);
 
wrapper.vm
.$nextTick()
.then(() => {
expect(findAllEnvironmentsDropdownItems()).toHaveLength(0);
done();
})
.catch(done.fail);
return wrapper.vm.$nextTick().then(() => {
expect(findAllEnvironmentsDropdownItems()).toHaveLength(0);
});
});
 
it('renders the datetimepicker dropdown', done => {
it('renders the datetimepicker dropdown', () => {
createMountedWrapper({ hasMetrics: true }, { stubs: ['graph-group', 'panel-type'] });
 
setupComponentStore(wrapper);
 
wrapper.vm
.$nextTick()
.then(() => {
expect(wrapper.find(DateTimePicker).exists()).toBe(true);
done();
})
.catch(done.fail);
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.find(DateTimePicker).exists()).toBe(true);
});
});
 
describe('when one of the metrics is missing', () => {
beforeEach(done => {
beforeEach(() => {
createShallowWrapper({ hasMetrics: true });
setupComponentStore(wrapper);
 
wrapper.vm.$nextTick(done);
return wrapper.vm.$nextTick();
});
 
it('shows a group empty area', () => {
Loading
Loading
@@ -300,7 +271,7 @@ describe('Dashboard', () => {
const resultEnvs = environmentData.filter(({ name }) => name.indexOf(searchTerm) !== -1);
setSearchTerm(searchTerm);
 
return wrapper.vm.$nextTick(() => {
return wrapper.vm.$nextTick().then(() => {
expect(findAllEnvironmentsDropdownItems().length).toEqual(resultEnvs.length);
});
});
Loading
Loading
@@ -349,12 +320,12 @@ describe('Dashboard', () => {
const findDraggablePanels = () => wrapper.findAll('.js-draggable-panel');
const findRearrangeButton = () => wrapper.find('.js-rearrange-button');
 
beforeEach(done => {
beforeEach(() => {
createShallowWrapper({ hasMetrics: true });
 
setupComponentStore(wrapper);
 
wrapper.vm.$nextTick(done);
return wrapper.vm.$nextTick();
});
 
it('wraps vuedraggable', () => {
Loading
Loading
@@ -368,9 +339,9 @@ describe('Dashboard', () => {
});
 
describe('when rearrange is enabled', () => {
beforeEach(done => {
beforeEach(() => {
wrapper.setProps({ rearrangePanelsAvailable: true });
wrapper.vm.$nextTick(done);
return wrapper.vm.$nextTick();
});
 
it('displays rearrange button', () => {
Loading
Loading
@@ -383,9 +354,9 @@ describe('Dashboard', () => {
.at(0)
.find('.js-draggable-remove');
 
beforeEach(done => {
beforeEach(() => {
findRearrangeButton().vm.$emit('click');
wrapper.vm.$nextTick(done);
return wrapper.vm.$nextTick();
});
 
it('it enables draggables', () => {
Loading
Loading
@@ -393,7 +364,7 @@ describe('Dashboard', () => {
expect(findEnabledDraggables()).toEqual(findDraggables());
});
 
it('metrics can be swapped', done => {
it('metrics can be swapped', () => {
const firstDraggable = findDraggables().at(0);
const mockMetrics = [...metricsDashboardPayload.panel_groups[1].panels];
 
Loading
Loading
@@ -404,33 +375,30 @@ describe('Dashboard', () => {
[mockMetrics[0], mockMetrics[1]] = [mockMetrics[1], mockMetrics[0]];
firstDraggable.vm.$emit('input', mockMetrics);
 
wrapper.vm.$nextTick(() => {
return wrapper.vm.$nextTick(() => {
const { panels } = wrapper.vm.dashboard.panel_groups[1];
 
expect(panels[1].title).toEqual(firstTitle);
expect(panels[0].title).toEqual(secondTitle);
done();
});
});
 
it('shows a remove button, which removes a panel', done => {
it('shows a remove button, which removes a panel', () => {
expect(findFirstDraggableRemoveButton().isEmpty()).toBe(false);
 
expect(findDraggablePanels().length).toEqual(expectedPanelCount);
findFirstDraggableRemoveButton().trigger('click');
 
wrapper.vm.$nextTick(() => {
return wrapper.vm.$nextTick(() => {
expect(findDraggablePanels().length).toEqual(expectedPanelCount - 1);
done();
});
});
 
it('it disables draggables when clicked again', done => {
it('it disables draggables when clicked again', () => {
findRearrangeButton().vm.$emit('click');
wrapper.vm.$nextTick(() => {
return wrapper.vm.$nextTick(() => {
expect(findRearrangeButton().attributes('pressed')).toBeFalsy();
expect(findEnabledDraggables().length).toBe(0);
done();
});
});
});
Loading
Loading
@@ -438,13 +406,13 @@ describe('Dashboard', () => {
});
 
describe('cluster health', () => {
beforeEach(done => {
beforeEach(() => {
mock.onGet(propsData.metricsEndpoint).reply(statusCodes.OK, JSON.stringify({}));
createShallowWrapper({ hasMetrics: true, showHeader: false });
 
// all_dashboards is not defined in health dashboards
wrapper.vm.$store.commit(`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, undefined);
wrapper.vm.$nextTick(done);
return wrapper.vm.$nextTick();
});
 
it('hides dashboard header by default', () => {
Loading
Loading
@@ -460,33 +428,29 @@ describe('Dashboard', () => {
describe('dashboard edit link', () => {
const findEditLink = () => wrapper.find('.js-edit-link');
 
beforeEach(done => {
beforeEach(() => {
createShallowWrapper({ hasMetrics: true });
 
wrapper.vm.$store.commit(
`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`,
dashboardGitResponse,
);
wrapper.vm.$nextTick(done);
return wrapper.vm.$nextTick();
});
 
it('is not present for the default dashboard', () => {
expect(findEditLink().exists()).toBe(false);
});
 
it('is present for a custom dashboard, and links to its edit_path', done => {
it('is present for a custom dashboard, and links to its edit_path', () => {
const dashboard = dashboardGitResponse[1]; // non-default dashboard
const currentDashboard = dashboard.path;
 
wrapper.setProps({ currentDashboard });
wrapper.vm
.$nextTick()
.then(() => {
expect(findEditLink().exists()).toBe(true);
expect(findEditLink().attributes('href')).toBe(dashboard.project_blob_path);
done();
})
.catch(done.fail);
return wrapper.vm.$nextTick().then(() => {
expect(findEditLink().exists()).toBe(true);
expect(findEditLink().attributes('href')).toBe(dashboard.project_blob_path);
});
});
});
 
Loading
Loading
@@ -498,18 +462,14 @@ describe('Dashboard', () => {
`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`,
dashboardGitResponse,
);
return wrapper.vm.$nextTick();
});
 
it('shows the dashboard dropdown', done => {
wrapper.vm
.$nextTick()
.then(() => {
const dashboardDropdown = wrapper.find(DashboardsDropdown);
it('shows the dashboard dropdown', () => {
const dashboardDropdown = wrapper.find(DashboardsDropdown);
 
expect(dashboardDropdown.exists()).toBe(true);
done();
})
.catch(done.fail);
expect(dashboardDropdown.exists()).toBe(true);
});
});
 
Loading
Loading
@@ -524,20 +484,16 @@ describe('Dashboard', () => {
},
{ stubs: ['graph-group', 'panel-type'] },
);
return wrapper.vm.$nextTick();
});
 
it('shows the link', done => {
wrapper.vm
.$nextTick()
.then(() => {
const externalDashboardButton = wrapper.find('.js-external-dashboard-link');
it('shows the link', () => {
const externalDashboardButton = wrapper.find('.js-external-dashboard-link');
 
expect(externalDashboardButton.exists()).toBe(true);
expect(externalDashboardButton.is(GlButton)).toBe(true);
expect(externalDashboardButton.text()).toContain('View full dashboard');
done();
})
.catch(done.fail);
expect(externalDashboardButton.exists()).toBe(true);
expect(externalDashboardButton.is(GlButton)).toBe(true);
expect(externalDashboardButton.text()).toContain('View full dashboard');
});
});
 
Loading
Loading
@@ -550,12 +506,12 @@ describe('Dashboard', () => {
.at(i)
.props('clipboardText');
 
beforeEach(done => {
beforeEach(() => {
createShallowWrapper({ hasMetrics: true, currentDashboard });
 
setupComponentStore(wrapper);
 
wrapper.vm.$nextTick(done);
return wrapper.vm.$nextTick();
});
 
it('contains a link to the dashboard', () => {
Loading
Loading
@@ -565,23 +521,21 @@ describe('Dashboard', () => {
expect(getClipboardTextAt(0)).toContain(`y_label=`);
});
 
it('strips the undefined parameter', done => {
it('strips the undefined parameter', () => {
wrapper.setProps({ currentDashboard: undefined });
 
wrapper.vm.$nextTick(() => {
return wrapper.vm.$nextTick(() => {
expect(getClipboardTextAt(0)).not.toContain(`dashboard=`);
expect(getClipboardTextAt(0)).toContain(`y_label=`);
done();
});
});
 
it('null parameter is stripped', done => {
it('null parameter is stripped', () => {
wrapper.setProps({ currentDashboard: null });
 
wrapper.vm.$nextTick(() => {
return wrapper.vm.$nextTick(() => {
expect(getClipboardTextAt(0)).not.toContain(`dashboard=`);
expect(getClipboardTextAt(0)).toContain(`y_label=`);
done();
});
});
});
Loading
Loading
Loading
Loading
@@ -131,20 +131,17 @@ describe('DashboardsDropdown', () => {
expect(findModal().contains(DuplicateDashboardForm)).toBe(true);
});
 
it('saves a new dashboard', done => {
it('saves a new dashboard', () => {
findModal().vm.$emit('ok', okEvent);
 
waitForPromises()
.then(() => {
expect(okEvent.preventDefault).toHaveBeenCalled();
expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.vm.$refs.duplicateDashboardModal.hide).toHaveBeenCalled();
expect(wrapper.emitted().selectDashboard).toBeTruthy();
expect(findAlert().exists()).toBe(false);
done();
})
.catch(done.fail);
return waitForPromises().then(() => {
expect(okEvent.preventDefault).toHaveBeenCalled();
expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.vm.$refs.duplicateDashboardModal.hide).toHaveBeenCalled();
expect(wrapper.emitted().selectDashboard).toBeTruthy();
expect(findAlert().exists()).toBe(false);
});
});
 
describe('when a new dashboard is saved succesfully', () => {
Loading
Loading
@@ -167,52 +164,42 @@ describe('DashboardsDropdown', () => {
findModal().vm.$emit('ok', okEvent);
};
 
it('to the default branch, redirects to the new dashboard', done => {
it('to the default branch, redirects to the new dashboard', () => {
submitForm({
branch: defaultBranch,
});
 
waitForPromises()
.then(() => {
expect(wrapper.emitted().selectDashboard[0][0]).toEqual(newDashboard);
done();
})
.catch(done.fail);
return waitForPromises().then(() => {
expect(wrapper.emitted().selectDashboard[0][0]).toEqual(newDashboard);
});
});
 
it('to a new branch refreshes in the current dashboard', done => {
it('to a new branch refreshes in the current dashboard', () => {
submitForm({
branch: 'another-branch',
});
 
waitForPromises()
.then(() => {
expect(wrapper.emitted().selectDashboard[0][0]).toEqual(dashboardGitResponse[0]);
done();
})
.catch(done.fail);
return waitForPromises().then(() => {
expect(wrapper.emitted().selectDashboard[0][0]).toEqual(dashboardGitResponse[0]);
});
});
});
 
it('handles error when a new dashboard is not saved', done => {
it('handles error when a new dashboard is not saved', () => {
const errMsg = 'An error occurred';
 
duplicateDashboardAction.mockRejectedValueOnce(errMsg);
findModal().vm.$emit('ok', okEvent);
 
waitForPromises()
.then(() => {
expect(okEvent.preventDefault).toHaveBeenCalled();
expect(findAlert().exists()).toBe(true);
expect(findAlert().text()).toBe(errMsg);
return waitForPromises().then(() => {
expect(okEvent.preventDefault).toHaveBeenCalled();
 
expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.vm.$refs.duplicateDashboardModal.hide).not.toHaveBeenCalled();
expect(findAlert().exists()).toBe(true);
expect(findAlert().text()).toBe(errMsg);
 
done();
})
.catch(done.fail);
expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.vm.$refs.duplicateDashboardModal.hide).not.toHaveBeenCalled();
});
});
 
it('id is correct, as the value of modal directive binding matches modal id', () => {
Loading
Loading
Loading
Loading
@@ -44,30 +44,27 @@ describe('DuplicateDashboardForm', () => {
describe('validates the file name', () => {
const findInvalidFeedback = () => findByRef('fileNameFormGroup').find('.invalid-feedback');
 
it('when is empty', done => {
it('when is empty', () => {
setValue('fileName', '');
wrapper.vm.$nextTick(() => {
return wrapper.vm.$nextTick(() => {
expect(findByRef('fileNameFormGroup').is('.is-valid')).toBe(true);
expect(findInvalidFeedback().exists()).toBe(false);
done();
});
});
 
it('when is valid', done => {
it('when is valid', () => {
setValue('fileName', 'my_dashboard.yml');
wrapper.vm.$nextTick(() => {
return wrapper.vm.$nextTick(() => {
expect(findByRef('fileNameFormGroup').is('.is-valid')).toBe(true);
expect(findInvalidFeedback().exists()).toBe(false);
done();
});
});
 
it('when is not valid', done => {
it('when is not valid', () => {
setValue('fileName', 'my_dashboard.exe');
wrapper.vm.$nextTick(() => {
return wrapper.vm.$nextTick(() => {
expect(findByRef('fileNameFormGroup').is('.is-invalid')).toBe(true);
expect(findInvalidFeedback().text()).toBeTruthy();
done();
});
});
});
Loading
Loading
@@ -124,30 +121,26 @@ describe('DuplicateDashboardForm', () => {
});
});
 
it('when a `default` branch option is set, branch input is invisible and ignored', done => {
it('when a `default` branch option is set, branch input is invisible and ignored', () => {
setChecked(wrapper.vm.$options.radioVals.DEFAULT);
setValue('branchName', 'a-new-branch');
 
expect(lastChange()).resolves.toMatchObject({
branch: defaultBranch,
});
wrapper.vm.$nextTick(() => {
return wrapper.vm.$nextTick(() => {
expect(findByRef('branchName').isVisible()).toBe(false);
done();
});
});
 
it('when `new` branch option is chosen, focuses on the branch name input', done => {
it('when `new` branch option is chosen, focuses on the branch name input', () => {
setChecked(wrapper.vm.$options.radioVals.NEW);
 
wrapper.vm
.$nextTick()
.then(() => {
wrapper.find('form').trigger('change');
expect(findByRef('branchName').is(':focus')).toBe(true);
})
.then(done)
.catch(done.fail);
return wrapper.vm.$nextTick().then(() => {
wrapper.find('form').trigger('change');
expect(findByRef('branchName').is(':focus')).toBe(true);
});
});
});
});
Loading
Loading
@@ -32,25 +32,23 @@ describe('Graph group component', () => {
expect(findCaretIcon().props('name')).toBe('angle-down');
});
 
it('should show the angle-right caret icon when the user collapses the group', done => {
it('should show the angle-right caret icon when the user collapses the group', () => {
wrapper.vm.collapse();
 
wrapper.vm.$nextTick(() => {
return wrapper.vm.$nextTick(() => {
expect(findContent().isVisible()).toBe(false);
expect(findCaretIcon().props('name')).toBe('angle-right');
done();
});
});
 
it('should show the open the group when collapseGroup is set to true', done => {
it('should show the open the group when collapseGroup is set to true', () => {
wrapper.setProps({
collapseGroup: true,
});
 
wrapper.vm.$nextTick(() => {
return wrapper.vm.$nextTick(() => {
expect(findContent().isVisible()).toBe(true);
expect(findCaretIcon().props('name')).toBe('angle-down');
done();
});
});
 
Loading
Loading
@@ -102,13 +100,12 @@ describe('Graph group component', () => {
expect(findCaretIcon().exists()).toBe(false);
});
 
it('should show the panel content when clicked', done => {
it('should show the panel content when clicked', () => {
wrapper.vm.collapse();
 
wrapper.vm.$nextTick(() => {
return wrapper.vm.$nextTick(() => {
expect(findContent().isVisible()).toBe(true);
expect(findCaretIcon().exists()).toBe(false);
done();
});
});
});
Loading
Loading
Loading
Loading
@@ -28,6 +28,8 @@ describe('Panel Type component', () => {
 
const exampleText = 'example_text';
 
const findCopyLink = () => wrapper.find({ ref: 'copyChartLink' });
const createWrapper = props => {
wrapper = shallowMount(PanelType, {
propsData: {
Loading
Loading
@@ -96,8 +98,7 @@ describe('Panel Type component', () => {
});
 
it('sets no clipboard copy link on dropdown by default', () => {
const link = () => wrapper.find({ ref: 'copyChartLink' });
expect(link().exists()).toBe(false);
expect(findCopyLink().exists()).toBe(false);
});
 
describe('Time Series Chart panel type', () => {
Loading
Loading
@@ -204,7 +205,6 @@ describe('Panel Type component', () => {
});
 
describe('when cliboard data is available', () => {
const link = () => wrapper.find({ ref: 'copyChartLink' });
const clipboardText = 'A value to copy.';
 
beforeEach(() => {
Loading
Loading
@@ -219,16 +219,16 @@ describe('Panel Type component', () => {
});
 
it('sets clipboard text on the dropdown', () => {
expect(link().exists()).toBe(true);
expect(link().element.dataset.clipboardText).toBe(clipboardText);
expect(findCopyLink().exists()).toBe(true);
expect(findCopyLink().element.dataset.clipboardText).toBe(clipboardText);
});
 
it('adds a copy button to the dropdown', () => {
expect(link().text()).toContain('Generate link to chart');
expect(findCopyLink().text()).toContain('Generate link to chart');
});
 
it('opens a toast on click', () => {
link().vm.$emit('click');
findCopyLink().vm.$emit('click');
 
expect(wrapper.vm.$toast.show).toHaveBeenCalled();
});
Loading
Loading
Loading
Loading
@@ -4,10 +4,9 @@ require 'spec_helper'
 
describe GitlabSchema.types['SnippetBlob'] do
it 'has the correct fields' do
expected_fields = [:highlighted_data, :raw_path,
:size, :binary, :name, :path,
:simple_viewer, :rich_viewer,
:mode]
expected_fields = [:highlighted_data, :plain_highlighted_data,
:raw_path, :size, :binary, :name, :path,
:simple_viewer, :rich_viewer, :mode]
 
is_expected.to have_graphql_fields(*expected_fields)
end
Loading
Loading
import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import axios from '~/lib/utils/axios_utils';
import environmentsComponent from '~/environments/components/environments_app.vue';
import { environment, folder } from './mock_data';
describe('Environment', () => {
const mockData = {
endpoint: 'environments.json',
canCreateEnvironment: true,
canReadEnvironment: true,
newEnvironmentPath: 'environments/new',
helpPagePath: 'help',
canaryDeploymentFeatureId: 'canary_deployment',
showCanaryDeploymentCallout: true,
userCalloutsPath: '/callouts',
lockPromotionSvgPath: '/assets/illustrations/lock-promotion.svg',
helpCanaryDeploymentsPath: 'help/canary-deployments',
};
let EnvironmentsComponent;
let component;
let mock;
beforeEach(() => {
mock = new MockAdapter(axios);
EnvironmentsComponent = Vue.extend(environmentsComponent);
});
afterEach(() => {
component.$destroy();
mock.restore();
});
describe('successful request', () => {
describe('without environments', () => {
beforeEach(done => {
mock.onGet(mockData.endpoint).reply(200, { environments: [] });
component = mountComponent(EnvironmentsComponent, mockData);
setTimeout(() => {
done();
}, 0);
});
it('should render the empty state', () => {
expect(component.$el.querySelector('.js-new-environment-button').textContent).toContain(
'New environment',
);
expect(component.$el.querySelector('.js-blank-state-title').textContent).toContain(
"You don't have any environments right now",
);
});
describe('when it is possible to enable a review app', () => {
beforeEach(done => {
mock
.onGet(mockData.endpoint)
.reply(200, { environments: [], review_app: { can_setup_review_app: true } });
component = mountComponent(EnvironmentsComponent, mockData);
setTimeout(() => {
done();
}, 0);
});
it('should render the enable review app button', () => {
expect(component.$el.querySelector('.js-enable-review-app-button').textContent).toContain(
'Enable review app',
);
});
});
});
describe('with paginated environments', () => {
beforeEach(done => {
mock.onGet(mockData.endpoint).reply(
200,
{
environments: [environment],
stopped_count: 1,
available_count: 0,
},
{
'X-nExt-pAge': '2',
'x-page': '1',
'X-Per-Page': '1',
'X-Prev-Page': '',
'X-TOTAL': '37',
'X-Total-Pages': '2',
},
);
component = mountComponent(EnvironmentsComponent, mockData);
setTimeout(() => {
done();
}, 0);
});
it('should render a table with environments', () => {
expect(component.$el.querySelectorAll('table')).not.toBeNull();
expect(component.$el.querySelector('.environment-name').textContent.trim()).toEqual(
environment.name,
);
});
describe('pagination', () => {
it('should render pagination', () => {
expect(component.$el.querySelectorAll('.gl-pagination li').length).toEqual(9);
});
it('should make an API request when page is clicked', done => {
spyOn(component, 'updateContent');
setTimeout(() => {
component.$el.querySelector('.gl-pagination li:nth-child(3) .page-link').click();
expect(component.updateContent).toHaveBeenCalledWith({ scope: 'available', page: '2' });
done();
}, 0);
});
it('should make an API request when using tabs', done => {
setTimeout(() => {
spyOn(component, 'updateContent');
component.$el.querySelector('.js-environments-tab-stopped').click();
expect(component.updateContent).toHaveBeenCalledWith({ scope: 'stopped', page: '1' });
done();
}, 0);
});
});
});
});
describe('unsuccessfull request', () => {
beforeEach(done => {
mock.onGet(mockData.endpoint).reply(500, {});
component = mountComponent(EnvironmentsComponent, mockData);
setTimeout(() => {
done();
}, 0);
});
it('should render empty state', () => {
expect(component.$el.querySelector('.js-blank-state-title').textContent).toContain(
"You don't have any environments right now",
);
});
});
describe('expandable folders', () => {
beforeEach(() => {
mock.onGet(mockData.endpoint).reply(
200,
{
environments: [folder],
stopped_count: 0,
available_count: 1,
},
{
'X-nExt-pAge': '2',
'x-page': '1',
'X-Per-Page': '1',
'X-Prev-Page': '',
'X-TOTAL': '37',
'X-Total-Pages': '2',
},
);
mock.onGet(environment.folder_path).reply(200, { environments: [environment] });
component = mountComponent(EnvironmentsComponent, mockData);
});
it('should open a closed folder', done => {
setTimeout(() => {
component.$el.querySelector('.folder-name').click();
Vue.nextTick(() => {
expect(component.$el.querySelector('.folder-icon.ic-chevron-right')).toBe(null);
done();
});
}, 0);
});
it('should close an opened folder', done => {
setTimeout(() => {
// open folder
component.$el.querySelector('.folder-name').click();
Vue.nextTick(() => {
// close folder
component.$el.querySelector('.folder-name').click();
Vue.nextTick(() => {
expect(component.$el.querySelector('.folder-icon.ic-chevron-down')).toBe(null);
done();
});
});
}, 0);
});
it('should show children environments and a button to show all environments', done => {
setTimeout(() => {
// open folder
component.$el.querySelector('.folder-name').click();
Vue.nextTick(() => {
// wait for next async request
setTimeout(() => {
expect(component.$el.querySelectorAll('.js-child-row').length).toEqual(1);
expect(component.$el.querySelector('.text-center > a.btn').textContent).toContain(
'Show all',
);
done();
});
});
}, 0);
});
});
describe('methods', () => {
beforeEach(() => {
mock.onGet(mockData.endpoint).reply(
200,
{
environments: [],
stopped_count: 0,
available_count: 1,
},
{},
);
component = mountComponent(EnvironmentsComponent, mockData);
spyOn(window.history, 'pushState').and.stub();
});
describe('updateContent', () => {
it('should set given parameters', done => {
component
.updateContent({ scope: 'stopped', page: '3' })
.then(() => {
expect(component.page).toEqual('3');
expect(component.scope).toEqual('stopped');
expect(component.requestData.scope).toEqual('stopped');
expect(component.requestData.page).toEqual('3');
done();
})
.catch(done.fail);
});
});
describe('onChangeTab', () => {
it('should set page to 1', () => {
spyOn(component, 'updateContent');
component.onChangeTab('stopped');
expect(component.updateContent).toHaveBeenCalledWith({ scope: 'stopped', page: '1' });
});
});
describe('onChangePage', () => {
it('should update page and keep scope', () => {
spyOn(component, 'updateContent');
component.onChangePage(4);
expect(component.updateContent).toHaveBeenCalledWith({ scope: component.scope, page: '4' });
});
});
});
});
Loading
Loading
@@ -652,10 +652,10 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
setup_import_export_config('light')
end
 
it 'does not import any instance-level services' do
it 'does not import any templated services' do
expect(restored_project_json).to eq(true)
 
expect(project.services.where(instance: true).count).to eq(0)
expect(project.services.where(template: true).count).to eq(0)
end
 
it 'imports labels' do
Loading
Loading
Loading
Loading
@@ -453,7 +453,7 @@ Service:
- updated_at
- active
- properties
- instance
- template
- push_events
- issues_events
- commit_events
Loading
Loading
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Serverless::Service do
let(:cluster) { create(:cluster) }
let(:environment) { create(:environment) }
let(:attributes) do
{
'apiVersion' => 'serving.knative.dev/v1alpha1',
'kind' => 'Service',
'metadata' => {
'creationTimestamp' => '2019-10-22T21:19:13Z',
'name' => 'kubetest',
'namespace' => 'project1-1-environment1'
},
'spec' => {
'runLatest' => {
'configuration' => {
'build' => {
'template' => {
'name' => 'some-image'
}
}
}
}
},
'environment_scope' => '*',
'cluster' => cluster,
'environment' => environment,
'podcount' => 0
}
end
it 'exposes methods extracting data from the attributes hash' do
service = Gitlab::Serverless::Service.new(attributes)
expect(service.name).to eq('kubetest')
expect(service.namespace).to eq('project1-1-environment1')
expect(service.environment_scope).to eq('*')
expect(service.podcount).to eq(0)
expect(service.created_at).to eq(DateTime.parse('2019-10-22T21:19:13Z'))
expect(service.image).to eq('some-image')
expect(service.cluster).to eq(cluster)
expect(service.environment).to eq(environment)
end
it 'returns nil for missing attributes' do
service = Gitlab::Serverless::Service.new({})
[:name, :namespace, :environment_scope, :cluster, :podcount, :created_at, :image, :description, :url, :environment].each do |method|
expect(service.send(method)).to be_nil
end
end
describe '#description' do
it 'extracts the description in knative 7 format if available' do
attributes = {
'spec' => {
'template' => {
'metadata' => {
'annotations' => {
'Description' => 'some description'
}
}
}
}
}
service = Gitlab::Serverless::Service.new(attributes)
expect(service.description).to eq('some description')
end
it 'extracts the description in knative 5/6 format if 7 is not available' do
attributes = {
'spec' => {
'runLatest' => {
'configuration' => {
'revisionTemplate' => {
'metadata' => {
'annotations' => {
'Description' => 'some description'
}
}
}
}
}
}
}
service = Gitlab::Serverless::Service.new(attributes)
expect(service.description).to eq('some description')
end
end
describe '#url' do
it 'returns proxy URL if cluster has serverless domain' do
# cluster = create(:cluster)
knative = create(:clusters_applications_knative, :installed, cluster: cluster)
create(:serverless_domain_cluster, clusters_applications_knative_id: knative.id)
service = Gitlab::Serverless::Service.new(attributes.merge('cluster' => cluster))
expect(Gitlab::Serverless::FunctionURI).to receive(:new).with(
function: service.name,
cluster: service.cluster.serverless_domain,
environment: service.environment
).and_return('https://proxy.example.com')
expect(service.url).to eq('https://proxy.example.com')
end
it 'returns the URL from the knative 6/7 format' do
attributes = {
'status' => {
'url' => 'https://example.com'
}
}
service = Gitlab::Serverless::Service.new(attributes)
expect(service.url).to eq('https://example.com')
end
it 'returns the URL from the knative 5 format' do
attributes = {
'status' => {
'domain' => 'example.com'
}
}
service = Gitlab::Serverless::Service.new(attributes)
expect(service.url).to eq('http://example.com')
end
end
end
Loading
Loading
@@ -18,7 +18,7 @@ describe Gitlab::UsageData do
create(:service, project: projects[1], type: 'SlackService', active: true)
create(:service, project: projects[2], type: 'SlackService', active: true)
create(:service, project: projects[2], type: 'MattermostService', active: false)
create(:service, project: projects[2], type: 'MattermostService', active: true, instance: true)
create(:service, project: projects[2], type: 'MattermostService', active: true, template: true)
create(:service, project: projects[2], type: 'CustomIssueTrackerService', active: true)
create(:project_error_tracking_setting, project: projects[0])
create(:project_error_tracking_setting, project: projects[1], enabled: false)
Loading
Loading
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20200206111847_migrate_propagate_service_template_sidekiq_queue.rb')
describe MigratePropagateServiceTemplateSidekiqQueue, :sidekiq, :redis do
include Gitlab::Database::MigrationHelpers
include StubWorker
context 'when there are jobs in the queue' do
it 'correctly migrates queue when migrating up' do
Sidekiq::Testing.disable! do
stub_worker(queue: 'propagate_service_template').perform_async('Something', [1])
stub_worker(queue: 'propagate_instance_level_service').perform_async('Something', [1])
described_class.new.up
expect(sidekiq_queue_length('propagate_service_template')).to eq 0
expect(sidekiq_queue_length('propagate_instance_level_service')).to eq 2
end
end
end
context 'when there are no jobs in the queues' do
it 'does not raise error when migrating up' do
expect { described_class.new.up }.not_to raise_error
end
end
end
Loading
Loading
@@ -97,23 +97,23 @@ describe Service do
end
end
 
describe "Instance" do
describe "Template" do
let(:project) { create(:project) }
 
describe '.build_from_instance' do
context 'when instance level integration is invalid' do
it 'sets instance level integration to inactive when instance is invalid' do
instance = build(:prometheus_service, instance: true, active: true, properties: {})
instance.save(validate: false)
describe '.build_from_template' do
context 'when template is invalid' do
it 'sets service template to inactive when template is invalid' do
template = build(:prometheus_service, template: true, active: true, properties: {})
template.save(validate: false)
 
service = described_class.build_from_instance(project.id, instance)
service = described_class.build_from_template(project.id, template)
 
expect(service).to be_valid
expect(service.active).to be false
end
end
 
describe 'build issue tracker from a instance level integration' do
describe 'build issue tracker from a template' do
let(:title) { 'custom title' }
let(:description) { 'custom description' }
let(:url) { 'http://jira.example.com' }
Loading
Loading
@@ -127,9 +127,9 @@ describe Service do
}
end
 
shared_examples 'integration creation from instance level' do
shared_examples 'service creation from a template' do
it 'creates a correct service' do
service = described_class.build_from_instance(project.id, instance_level_integration)
service = described_class.build_from_template(project.id, template)
 
expect(service).to be_active
expect(service.title).to eq(title)
Loading
Loading
@@ -144,38 +144,38 @@ describe Service do
# this will be removed as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
context 'when data are stored in properties' do
let(:properties) { data_params.merge(title: title, description: description) }
let!(:instance_level_integration) do
create(:jira_service, :without_properties_callback, instance: true, properties: properties.merge(additional: 'something'))
let!(:template) do
create(:jira_service, :without_properties_callback, template: true, properties: properties.merge(additional: 'something'))
end
 
it_behaves_like 'integration creation from instance level'
it_behaves_like 'service creation from a template'
end
 
context 'when data are stored in separated fields' do
let(:instance_level_integration) do
create(:jira_service, data_params.merge(properties: {}, title: title, description: description, instance: true))
let(:template) do
create(:jira_service, data_params.merge(properties: {}, title: title, description: description, template: true))
end
 
it_behaves_like 'integration creation from instance level'
it_behaves_like 'service creation from a template'
end
 
context 'when data are stored in both properties and separated fields' do
let(:properties) { data_params.merge(title: title, description: description) }
let(:instance_level_integration) do
create(:jira_service, :without_properties_callback, active: true, instance: true, properties: properties).tap do |service|
let(:template) do
create(:jira_service, :without_properties_callback, active: true, template: true, properties: properties).tap do |service|
create(:jira_tracker_data, data_params.merge(service: service))
end
end
 
it_behaves_like 'integration creation from instance level'
it_behaves_like 'service creation from a template'
end
end
end
 
describe "for pushover service" do
let!(:instance_level_integration) do
let!(:service_template) do
PushoverService.create(
instance: true,
template: true,
properties: {
device: 'MyDevice',
sound: 'mic',
Loading
Loading
@@ -188,7 +188,7 @@ describe Service do
it "has all fields prefilled" do
service = project.find_or_initialize_service('pushover')
 
expect(service.instance).to eq(false)
expect(service.template).to eq(false)
expect(service.device).to eq('MyDevice')
expect(service.sound).to eq('mic')
expect(service.priority).to eq(4)
Loading
Loading
@@ -391,6 +391,14 @@ describe Service do
end
end
 
describe '.find_by_template' do
let!(:service) { create(:service, template: true) }
it 'returns service template' do
expect(described_class.find_by_template).to eq(service)
end
end
describe '#api_field_names' do
let(:fake_service) do
Class.new(Service) do
Loading
Loading
Loading
Loading
@@ -18,7 +18,7 @@ describe SnippetBlobPresenter do
snippet.file_name = 'test.md'
snippet.content = '*foo*'
 
expect(subject).to eq '<p data-sourcepos="1:1-1:5" dir="auto"><em>foo</em></p>'
expect(subject).to eq '<span id="LC1" class="line" lang="markdown"><span class="ge">*foo*</span></span>'
end
 
it 'returns syntax highlighted content' do
Loading
Loading
@@ -33,7 +33,41 @@ describe SnippetBlobPresenter do
snippet.file_name = 'test'
snippet.content = 'foo'
 
expect(described_class.new(snippet.blob).highlighted_data).to eq '<span id="LC1" class="line" lang="plaintext">foo</span>'
expect(subject).to eq '<span id="LC1" class="line" lang="plaintext">foo</span>'
end
end
describe '#plain_highlighted_data' do
let(:snippet) { build(:personal_snippet) }
subject { described_class.new(snippet.blob).plain_highlighted_data }
it 'returns nil when the snippet blob is binary' do
allow(snippet.blob).to receive(:binary?).and_return(true)
expect(subject).to be_nil
end
it 'returns plain content when snippet file is markup' do
snippet.file_name = 'test.md'
snippet.content = '*foo*'
expect(subject).to eq '<span id="LC1" class="line" lang="">*foo*</span>'
end
it 'returns plain syntax content' do
snippet.file_name = 'test.rb'
snippet.content = 'class Foo;end'
expect(subject)
.to eq '<span id="LC1" class="line" lang="">class Foo;end</span>'
end
it 'returns plain text highlighted content' do
snippet.file_name = 'test'
snippet.content = 'foo'
expect(subject).to eq '<span id="LC1" class="line" lang="">foo</span>'
end
end
 
Loading
Loading
Loading
Loading
@@ -15,7 +15,7 @@ describe Projects::CreateService, '#execute' do
}
end
 
it 'creates labels on Project creation if there are instance level services' do
it 'creates labels on Project creation if there are templates' do
Label.create(title: "bug", template: true)
project = create_project(user, opts)
 
Loading
Loading
@@ -96,7 +96,7 @@ describe Projects::CreateService, '#execute' do
end
 
it 'sets invalid service as inactive' do
create(:service, type: 'JiraService', project: nil, instance: true, active: true)
create(:service, type: 'JiraService', project: nil, template: true, active: true)
 
project = create_project(user, opts)
service = project.services.first
Loading
Loading
@@ -342,22 +342,22 @@ describe Projects::CreateService, '#execute' do
end
end
 
context 'when there is an active instance level service' do
context 'when there is an active service template' do
before do
create(:service, project: nil, instance: true, active: true)
create(:service, project: nil, template: true, active: true)
end
 
it 'creates a service from instance level service' do
it 'creates a service from this template' do
project = create_project(user, opts)
 
expect(project.services.count).to eq 1
end
end
 
context 'when a bad instance level service is created' do
context 'when a bad service template is created' do
it 'sets service to be inactive' do
opts[:import_url] = 'http://www.gitlab.com/gitlab-org/gitlab-foss'
create(:service, type: 'DroneCiService', project: nil, instance: true, active: true)
create(:service, type: 'DroneCiService', project: nil, template: true, active: true)
 
project = create_project(user, opts)
service = project.services.first
Loading
Loading
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