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