From 4546ecd67ab2497af51144120667737e773e2dcc Mon Sep 17 00:00:00 2001 From: Winnie Hellmann <winnie@gitlab.com> Date: Tue, 1 Aug 2017 18:06:06 +0000 Subject: [PATCH] Derive project path from import URL --- .../javascripts/projects/project_new.js | 64 +++++++-- .../unreleased/winh-derive-project-name.yml | 4 + spec/javascripts/projects/project_new_spec.js | 127 ++++++++++++++++++ 3 files changed, 184 insertions(+), 11 deletions(-) create mode 100644 changelogs/unreleased/winh-derive-project-name.yml create mode 100644 spec/javascripts/projects/project_new_spec.js diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js index 2091b275c3d..1dc1dbf356d 100644 --- a/app/assets/javascripts/projects/project_new.js +++ b/app/assets/javascripts/projects/project_new.js @@ -1,6 +1,40 @@ -document.addEventListener('DOMContentLoaded', () => { +let hasUserDefinedProjectPath = false; + +const deriveProjectPathFromUrl = ($projectImportUrl, $projectPath) => { + if ($projectImportUrl.attr('disabled') || hasUserDefinedProjectPath) { + return; + } + + let importUrl = $projectImportUrl.val().trim(); + if (importUrl.length === 0) { + return; + } + + /* + \/?: remove trailing slash + (\.git\/?)?: remove trailing .git (with optional trailing slash) + (\?.*)?: remove query string + (#.*)?: remove fragment identifier + */ + importUrl = importUrl.replace(/\/?(\.git\/?)?(\?.*)?(#.*)?$/, ''); + + // extract everything after the last slash + const pathMatch = /\/([^/]+)$/.exec(importUrl); + if (pathMatch) { + $projectPath.val(pathMatch[1]); + } +}; + +const bindEvents = () => { + const $newProjectForm = $('#new_project'); const importBtnTooltip = 'Please enter a valid project name.'; const $importBtnWrapper = $('.import_gitlab_project'); + const $projectImportUrl = $('#project_import_url'); + const $projectPath = $('#project_path'); + + if ($newProjectForm.length !== 1) { + return; + } $('.how_to_import_link').on('click', (e) => { e.preventDefault(); @@ -13,19 +47,19 @@ document.addEventListener('DOMContentLoaded', () => { $('.btn_import_gitlab_project').on('click', () => { const importHref = $('a.btn_import_gitlab_project').attr('href'); - $('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$('#project_path').val()}`); + $('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$projectPath.val()}`); }); - $('.btn_import_gitlab_project').attr('disabled', !$('#project_path').val().trim().length); + $('.btn_import_gitlab_project').attr('disabled', !$projectPath.val().trim().length); $importBtnWrapper.attr('title', importBtnTooltip); - $('#new_project').on('submit', () => { - const $path = $('#project_path'); - $path.val($path.val().trim()); + $newProjectForm.on('submit', () => { + $projectPath.val($projectPath.val().trim()); }); - $('#project_path').on('keyup', () => { - if ($('#project_path').val().trim().length) { + $projectPath.on('keyup', () => { + hasUserDefinedProjectPath = $projectPath.val().trim().length > 0; + if (hasUserDefinedProjectPath) { $('.btn_import_gitlab_project').attr('disabled', false); $importBtnWrapper.attr('title', ''); $importBtnWrapper.removeClass('has-tooltip'); @@ -35,9 +69,17 @@ document.addEventListener('DOMContentLoaded', () => { } }); - $('#project_import_url').disable(); + $projectImportUrl.disable(); + $projectImportUrl.keyup(() => deriveProjectPathFromUrl($projectImportUrl, $projectPath)); + $('.import_git').on('click', () => { - const $projectImportUrl = $('#project_import_url'); $projectImportUrl.attr('disabled', !$projectImportUrl.attr('disabled')); }); -}); +}; + +document.addEventListener('DOMContentLoaded', bindEvents); + +export default { + bindEvents, + deriveProjectPathFromUrl, +}; diff --git a/changelogs/unreleased/winh-derive-project-name.yml b/changelogs/unreleased/winh-derive-project-name.yml new file mode 100644 index 00000000000..2244d21d768 --- /dev/null +++ b/changelogs/unreleased/winh-derive-project-name.yml @@ -0,0 +1,4 @@ +--- +title: Derive project path from import URL +merge_request: 13131 +author: diff --git a/spec/javascripts/projects/project_new_spec.js b/spec/javascripts/projects/project_new_spec.js new file mode 100644 index 00000000000..850768f0e4f --- /dev/null +++ b/spec/javascripts/projects/project_new_spec.js @@ -0,0 +1,127 @@ +import projectNew from '~/projects/project_new'; + +describe('New Project', () => { + let $projectImportUrl; + let $projectPath; + + beforeEach(() => { + setFixtures(` + <input id="project_import_url" /> + <input id="project_path" /> + `); + + $projectImportUrl = $('#project_import_url'); + $projectPath = $('#project_path'); + }); + + describe('deriveProjectPathFromUrl', () => { + const dummyImportUrl = `${gl.TEST_HOST}/dummy/import/url.git`; + + beforeEach(() => { + projectNew.bindEvents(); + $projectPath.val('').keyup().val(dummyImportUrl); + }); + + it('does not change project path for disabled $projectImportUrl', () => { + $projectImportUrl.attr('disabled', true); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual(dummyImportUrl); + }); + + describe('for enabled $projectImportUrl', () => { + beforeEach(() => { + $projectImportUrl.attr('disabled', false); + }); + + it('does not change project path if it is set by user', () => { + $projectPath.keyup(); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual(dummyImportUrl); + }); + + it('does not change project path for empty $projectImportUrl', () => { + $projectImportUrl.val(''); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual(dummyImportUrl); + }); + + it('does not change project path for whitespace $projectImportUrl', () => { + $projectImportUrl.val(' '); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual(dummyImportUrl); + }); + + it('does not change project path for $projectImportUrl without slashes', () => { + $projectImportUrl.val('has-no-slash'); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual(dummyImportUrl); + }); + + it('changes project path to last $projectImportUrl component', () => { + $projectImportUrl.val('/this/is/last'); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual('last'); + }); + + it('ignores trailing slashes in $projectImportUrl', () => { + $projectImportUrl.val('/has/trailing/slash/'); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual('slash'); + }); + + it('ignores fragment identifier in $projectImportUrl', () => { + $projectImportUrl.val('/this/has/a#fragment-identifier/'); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual('a'); + }); + + it('ignores query string in $projectImportUrl', () => { + $projectImportUrl.val('/url/with?query=string'); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual('with'); + }); + + it('ignores trailing .git in $projectImportUrl', () => { + $projectImportUrl.val('/repository.git'); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual('repository'); + }); + + it('changes project path for HTTPS URL in $projectImportUrl', () => { + $projectImportUrl.val('https://username:password@gitlab.company.com/group/project.git'); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual('project'); + }); + + it('changes project path for SSH URL in $projectImportUrl', () => { + $projectImportUrl.val('git@gitlab.com:gitlab-org/gitlab-ce.git'); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual('gitlab-ce'); + }); + }); + }); +}); -- GitLab