Commit a557f4bf authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab-ce master

parents 0e44382e 6d4f33ce
......@@ -12,6 +12,7 @@ const Api = {
groupProjectsPath: '/api/:version/groups/:id/projects.json',
projectsPath: '/api/:version/projects.json',
projectPath: '/api/:version/projects/:id',
forkedProjectsPath: '/api/:version/projects/:id/forks',
projectLabelsPath: '/:namespace_path/:project_path/-/labels',
projectMergeRequestsPath: '/api/:version/projects/:id/merge_requests',
projectMergeRequestPath: '/api/:version/projects/:id/merge_requests/:mrid',
......@@ -113,6 +114,21 @@ const Api = {
return axios.get(url);
},
/**
* Get all projects for a forked relationship to a specified project
* @param {string} projectPath - Path or ID of a project
* @param {Object} params - Get request parameters
* @returns {Promise} - Request promise
*/
projectForks(projectPath, params) {
const url = Api.buildUrl(Api.forkedProjectsPath).replace(
':id',
encodeURIComponent(projectPath),
);
return axios.get(url, { params });
},
/**
* Get all Merge Requests for a project, eventually filtering based on
* supplied parameters
......
......@@ -5,19 +5,24 @@ const { EOL } = require('os');
const program = require('commander');
const chalk = require('chalk');
const SUCCESS_CODE = 0;
const JEST_ROUTE = 'spec/frontend';
const KARMA_ROUTE = 'spec/javascripts';
const COMMON_ARGS = ['--colors'];
const JEST_ARGS = ['--passWithNoTests'];
const KARMA_ARGS = ['--no-fail-on-empty-test-suite'];
const SUCCESS_CODE = 0;
const jestArgs = [...COMMON_ARGS, '--passWithNoTests'];
const karmaArgs = [...COMMON_ARGS, '--no-fail-on-empty-test-suite'];
program
.version('0.1.0')
.usage('[options] <file ...>')
.option('-p, --parallel', 'Run tests suites in parallel')
.option(
'-w, --watch',
'Rerun tests when files change (tests will be run in parallel if this enabled)',
)
.parse(process.argv);
const shouldParallelize = program.parallel || program.watch;
const isSuccess = code => code === SUCCESS_CODE;
const combineExitCodes = codes => {
......@@ -31,7 +36,7 @@ const skipIfFail = fn => code => (isSuccess(code) ? fn() : code);
const endWithEOL = str => (str[str.length - 1] === '\n' ? str : `${str}${EOL}`);
const runTests = paths => {
if (program.parallel) {
if (shouldParallelize) {
return Promise.all([runJest(paths), runKarma(paths)]).then(combineExitCodes);
} else {
return runJest(paths).then(skipIfFail(() => runKarma(paths)));
......@@ -73,11 +78,11 @@ const spawnYarnScript = (cmd, args) => {
};
const runJest = args => {
return spawnYarnScript('jest', [...JEST_ARGS, ...COMMON_ARGS, ...toJestArgs(args)]);
return spawnYarnScript('jest', [...jestArgs, ...toJestArgs(args)]);
};
const runKarma = args => {
return spawnYarnScript('karma', [...KARMA_ARGS, ...COMMON_ARGS, ...toKarmaArgs(args)]);
return spawnYarnScript('karma', [...karmaArgs, ...toKarmaArgs(args)]);
};
const replacePath = to => path =>
......@@ -96,6 +101,10 @@ const toKarmaArgs = paths =>
paths.reduce((acc, path) => acc.concat('-f', replacePathForKarma(path)), []);
const main = paths => {
if (program.watch) {
jestArgs.push('--watch');
karmaArgs.push('--single-run', 'false', '--auto-watch');
}
runTests(paths).then(code => {
console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
if (isSuccess(code)) {
......
......@@ -474,4 +474,27 @@ describe('Api', () => {
.catch(done.fail);
});
});
describe('projectForks', () => {
it('gets forked projects', done => {
const dummyProjectPath = 'gitlab-org/gitlab-ce';
const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/projects/${encodeURIComponent(
dummyProjectPath,
)}/forks`;
jest.spyOn(axios, 'get');
mock.onGet(expectedUrl).replyOnce(200, ['fork']);
Api.projectForks(dummyProjectPath, { visibility: 'private' })
.then(({ data }) => {
expect(data).toEqual(['fork']);
expect(axios.get).toHaveBeenCalledWith(expectedUrl, {
params: { visibility: 'private' },
});
})
.then(done)
.catch(done.fail);
});
});
});
import { mountComponentWithRender } from 'spec/helpers/vue_mount_component_helper';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
const TEST_TITLE = 'lorem-ipsum-dolar-sit-amit-consectur-adipiscing-elit-sed-do';
const CLASS_SHOW_TOOLTIP = 'js-show-tooltip';
const STYLE_TRUNCATED = {
display: 'inline-block',
'max-width': '20px',
};
const STYLE_NORMAL = {
display: 'inline-block',
'max-width': '1000px',
};
function mountTooltipOnTruncate(options, createChildren) {
return mountComponentWithRender(h => h(TooltipOnTruncate, options, createChildren(h)), '#app');
}
const STYLE_TRUNCATED = 'display: inline-block; max-width: 20px;';
const STYLE_NORMAL = 'display: inline-block; max-width: 1000px;';
describe('TooltipOnTruncate component', () => {
let vm;
const localVue = createLocalVue();
const createElementWithStyle = (style, content) => `<a href="#" style="${style}">${content}</a>`;
beforeEach(() => {
const el = document.createElement('div');
el.id = 'app';
document.body.appendChild(el);
describe('TooltipOnTruncate component', () => {
let wrapper;
const createComponent = ({ propsData, ...options } = {}) => {
wrapper = shallowMount(localVue.extend(TooltipOnTruncate), {
localVue,
sync: false,
attachToDocument: true,
propsData: {
title: TEST_TITLE,
...propsData,
},
...options,
});
};
afterEach(() => {
vm.$destroy();
wrapper.destroy();
});
const hasTooltip = () => wrapper.classes('js-show-tooltip');
describe('with default target', () => {
it('renders tooltip if truncated', done => {
const options = {
createComponent({
attrs: {
style: STYLE_TRUNCATED,
props: {
title: TEST_TITLE,
},
};
vm = mountTooltipOnTruncate(options, () => [TEST_TITLE]);
slots: {
default: [TEST_TITLE],
},
});
vm.$nextTick()
wrapper.vm
.$nextTick()
.then(() => {
expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP);
expect(vm.$el).toHaveData('original-title', TEST_TITLE);
expect(vm.$el).toHaveData('placement', 'top');
expect(hasTooltip()).toBe(true);
expect(wrapper.attributes('data-original-title')).toEqual(TEST_TITLE);
expect(wrapper.attributes('data-placement')).toEqual('top');
})
.then(done)
.catch(done.fail);
});
it('does not render tooltip if normal', done => {
const options = {
createComponent({
attrs: {
style: STYLE_NORMAL,
props: {
title: TEST_TITLE,
},
};
vm = mountTooltipOnTruncate(options, () => [TEST_TITLE]);
slots: {
default: [TEST_TITLE],
},
});
vm.$nextTick()
wrapper.vm
.$nextTick()
.then(() => {
expect(vm.$el).not.toHaveClass(CLASS_SHOW_TOOLTIP);
expect(hasTooltip()).toBe(false);
})
.then(done)
.catch(done.fail);
......@@ -71,37 +75,41 @@ describe('TooltipOnTruncate component', () => {
describe('with child target', () => {
it('renders tooltip if truncated', done => {
const options = {
createComponent({
attrs: {
style: STYLE_NORMAL,
props: {
title: TEST_TITLE,
},
propsData: {
truncateTarget: 'child',
},
};
vm = mountTooltipOnTruncate(options, h => [h('a', { style: STYLE_TRUNCATED }, TEST_TITLE)]);
slots: {
default: createElementWithStyle(STYLE_TRUNCATED, TEST_TITLE),
},
});
vm.$nextTick()
wrapper.vm
.$nextTick()
.then(() => {
expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP);
expect(hasTooltip()).toBe(true);
})
.then(done)
.catch(done.fail);
});
it('does not render tooltip if normal', done => {
const options = {
props: {
title: TEST_TITLE,
createComponent({
propsData: {
truncateTarget: 'child',
},
};
vm = mountTooltipOnTruncate(options, h => [h('a', { style: STYLE_NORMAL }, TEST_TITLE)]);
slots: {
default: createElementWithStyle(STYLE_NORMAL, TEST_TITLE),
},
});
vm.$nextTick()
wrapper.vm
.$nextTick()
.then(() => {
expect(vm.$el).not.toHaveClass(CLASS_SHOW_TOOLTIP);
expect(hasTooltip()).toBe(false);
})
.then(done)
.catch(done.fail);
......@@ -110,22 +118,25 @@ describe('TooltipOnTruncate component', () => {
describe('with fn target', () => {
it('renders tooltip if truncated', done => {
const options = {
createComponent({
attrs: {
style: STYLE_NORMAL,
props: {
title: TEST_TITLE,
},
propsData: {
truncateTarget: el => el.childNodes[1],
},
};
vm = mountTooltipOnTruncate(options, h => [
h('a', { style: STYLE_NORMAL }, TEST_TITLE),
h('span', { style: STYLE_TRUNCATED }, TEST_TITLE),
]);
slots: {
default: [
createElementWithStyle('', TEST_TITLE),
createElementWithStyle(STYLE_TRUNCATED, TEST_TITLE),
],
},
});
vm.$nextTick()
wrapper.vm
.$nextTick()
.then(() => {
expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP);
expect(hasTooltip()).toBe(true);
})
.then(done)
.catch(done.fail);
......@@ -134,20 +145,25 @@ describe('TooltipOnTruncate component', () => {
describe('placement', () => {
it('sets data-placement when tooltip is rendered', done => {
const options = {
props: {
title: TEST_TITLE,
truncateTarget: 'child',
placement: 'bottom',
},
};
const placement = 'bottom';
vm = mountTooltipOnTruncate(options, h => [h('a', { style: STYLE_TRUNCATED }, TEST_TITLE)]);
createComponent({
propsData: {
placement,
},
attrs: {
style: STYLE_TRUNCATED,
},
slots: {
default: TEST_TITLE,
},
});
vm.$nextTick()
wrapper.vm
.$nextTick()
.then(() => {
expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP);
expect(vm.$el).toHaveData('placement', options.props.placement);
expect(hasTooltip()).toBe(true);
expect(wrapper.attributes('data-placement')).toEqual(placement);
})
.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