diff --git a/assets/src/bundles/webapp/webapp-utils.js b/assets/src/bundles/webapp/webapp-utils.js --- a/assets/src/bundles/webapp/webapp-utils.js +++ b/assets/src/bundles/webapp/webapp-utils.js @@ -1,5 +1,5 @@ /** - * Copyright (C) 2018-2020 The Software Heritage developers + * Copyright (C) 2018-2021 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information @@ -53,6 +53,33 @@ $('body').css('padding-bottom', $('footer').outerHeight() + 'px'); } +function fixSearchInputMalformedSWHID(searchInputElt) { + const searchQueryText = $(searchInputElt).val().trim(); + if (!searchQueryText.startsWith('swh:')) { + return; + } + const swhidWithQualifiers = searchQueryText.indexOf(';') !== -1; + if (swhidWithQualifiers) { + const lastQualifier = searchQueryText.split(';').pop(); + if (lastQualifier === undefined) { + return; + } + const splittedQualifier = lastQualifier.split('='); + if (splittedQualifier.length !== 2) { + return; + } + const [qualifierName, qualifierValue] = splittedQualifier; + let fixedSWHIDFormat = searchQueryText; + if ((new Set(['anchor', 'lines', 'visit']).has(qualifierName) && qualifierValue.endsWith('/')) || + (new Set(['origin', 'path']).has(qualifierName) && qualifierValue.endsWith('//'))) { + fixedSWHIDFormat = fixedSWHIDFormat.slice(0, -1); + } + if (fixedSWHIDFormat !== searchQueryText) { + $(searchInputElt).val(fixedSWHIDFormat); + } + } +} + $(document).ready(() => { // redirect to last browse page if any when clicking on the 'Browse' entry // in the sidebar @@ -176,6 +203,12 @@ window.location = `${Urls.browse_search()}?${queryParameters.toString()}`; }); + // handle possible malformed input SWHID in search box after copy paste + // https://forge.softwareheritage.org/T3234 + $('#swh-origins-url-patterns,#swh-origins-search-top-input').change(event => { + fixSearchInputMalformedSWHID(event.target); + }); + }); export function initPage(page) { diff --git a/cypress/integration/origin-search.spec.js b/cypress/integration/origin-search.spec.js --- a/cypress/integration/origin-search.spec.js +++ b/cypress/integration/origin-search.spec.js @@ -500,4 +500,28 @@ }); }); + context('Test fixable malformed SWHIDs', function() { + + function checkMalformedSWHIDgetsFixed(url, searchInputElt, repoData) { + const malformedSWHIDs = [`swh:1:cnt:${repoData.content[0].sha1git};lines=45-60/`, + `swh:1:cnt:${repoData.content[0].sha1git};origin=https://example.org/project//`]; + for (let malformedSWHID of malformedSWHIDs) { + cy.visit(url); + cy.get(searchInputElt) + .type(malformedSWHID); + // to trigger input change event + cy.get('body').click(); + cy.get(searchInputElt) + .should('have.value', malformedSWHID.slice(0, -1)); + } + } + + it('should remove invalid trailing slash when copying a malformed SWHID in search page input', function() { + checkMalformedSWHIDgetsFixed(this.Urls.browse_search(), '#swh-origins-url-patterns', this.unarchivedRepo); + }); + + it('should remove invalid trailing slash when copying a malformed SWHID in top right search input', function() { + checkMalformedSWHIDgetsFixed(this.Urls.browse_help(), '#swh-origins-search-top-input', this.unarchivedRepo); + }); + }); });