diff --git a/assets/src/bundles/add_forge/create-request.js b/assets/src/bundles/add_forge/create-request.js index 34b3d4f6..f2e57bb5 100644 --- a/assets/src/bundles/add_forge/create-request.js +++ b/assets/src/bundles/add_forge/create-request.js @@ -1,92 +1,113 @@ /** * Copyright (C) 2022 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 */ import {handleFetchError, removeUrlFragment, csrfPost} from 'utils/functions'; let requestBrowseTable; export function onCreateRequestPageLoad() { $(document).ready(() => { $('#requestCreateForm').submit(async function(event) { event.preventDefault(); try { const response = await csrfPost($(this).attr('action'), {'Content-Type': 'application/x-www-form-urlencoded'}, $(this).serialize()); handleFetchError(response); $('#userMessageDetail').empty(); $('#userMessage').text('Your request has been submitted'); $('#userMessage').removeClass('badge-danger'); $('#userMessage').addClass('badge-success'); requestBrowseTable.draw(); // redraw the table to update the list - } catch (response) { - const responseText = await response.json(); + } catch (errorResponse) { $('#userMessageDetail').empty(); - $('#userMessage').text('Sorry; an error occurred'); - $('#userMessageDetail').text(responseText.substring(0, 500)); + + let errorMessage; + let errorMessageDetail = ''; + const errorData = await errorResponse.json(); + // if (errorResponse.content_type === 'text/plain') { // does not work? + if (errorResponse.status === 409) { + errorMessage = errorData; + } else { // assuming json response + // const exception = errorData['exception']; + errorMessage = 'An unknown error occurred during the request creation'; + try { + const reason = JSON.parse(errorData['reason']); + Object.entries(reason).forEach((keys, _) => { + const key = keys[0]; + const message = keys[1][0]; // take only the first issue + errorMessageDetail += `\n${key}: ${message}`; + }); + } catch (_) { + errorMessageDetail = errorData['reason']; // can't parse it, leave it raw + } + } + $('#userMessage').text( + errorMessageDetail ? `Error: ${errorMessageDetail}` : errorMessage + ); $('#userMessage').removeClass('badge-success'); $('#userMessage').addClass('badge-danger'); } }); $(window).on('hashchange', () => { if (window.location.hash === '#browse-requests') { $('.nav-tabs a[href="#swh-add-forge-requests-list"]').tab('show'); } else { $('.nav-tabs a[href="#swh-add-forge-submit-request"]').tab('show'); } }); $('#swh-add-forge-requests-list-tab').on('shown.bs.tab', () => { window.location.hash = '#browse-requests'; }); $('#swh-add-forge-tab').on('shown.bs.tab', () => { removeUrlFragment(); }); populateRequestBrowseList(); // Load existing requests }); } export function populateRequestBrowseList() { requestBrowseTable = $('#add-forge-request-browse') .on('error.dt', (e, settings, techNote, message) => { $('#add-forge-browse-request-error').text(message); }) .DataTable({ serverSide: true, processing: true, retrieve: true, searching: true, info: false, dom: '<<"d-flex justify-content-between align-items-center"f' + '<"#list-exclude">l>rt<"bottom"ip>>', ajax: { 'url': Urls.add_forge_request_list_datatables() }, columns: [ { data: 'submission_date', name: 'submission_date' }, { data: 'forge_type', name: 'forge_type' }, { data: 'forge_url', name: 'forge_url' }, { data: 'status', name: 'status' } ] }); requestBrowseTable.draw(); } diff --git a/cypress/integration/add-forge-now-request-create.spec.js b/cypress/integration/add-forge-now-request-create.spec.js index 630554b4..ace2bcd7 100644 --- a/cypress/integration/add-forge-now-request-create.spec.js +++ b/cypress/integration/add-forge-now-request-create.spec.js @@ -1,139 +1,166 @@ /** * Copyright (C) 2022 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 */ function populateForm(type, url, contact, email, consent, comment) { cy.get('#swh-input-forge-type').select(type); cy.get('#swh-input-forge-url').type(url); cy.get('#swh-input-forge-contact-name').type(contact); cy.get('#swh-input-forge-contact-email').type(email); cy.get('#swh-input-forge-comment').type(comment); } describe('Test add-forge-request creation', function() { beforeEach(function() { this.addForgeNowUrl = this.Urls.forge_add(); }); it('should show both tabs for every user', function() { cy.visit(this.addForgeNowUrl); cy.get('#swh-add-forge-tab') .should('have.class', 'nav-link'); cy.get('#swh-add-forge-requests-list-tab') .should('have.class', 'nav-link'); }); it('should show create forge tab by default', function() { cy.visit(this.addForgeNowUrl); cy.get('#swh-add-forge-tab') .should('have.class', 'active'); cy.get('#swh-add-forge-requests-list-tab') .should('not.have.class', 'active'); }); it('should show login link for anonymous user', function() { cy.visit(this.addForgeNowUrl); cy.get('#loginLink') .should('be.visible') .should('contain', 'log in'); }); it('should bring back after login', function() { cy.visit(this.addForgeNowUrl); cy.get('#loginLink') .should('have.attr', 'href') .and('include', `${this.Urls.login()}?next=${this.Urls.forge_add()}`); }); it('should change tabs on click', function() { cy.visit(this.addForgeNowUrl); cy.get('#swh-add-forge-requests-list-tab').click(); cy.get('#swh-add-forge-tab') .should('not.have.class', 'active'); cy.get('#swh-add-forge-requests-list-tab') .should('have.class', 'active'); cy.get('#swh-add-forge-tab').click(); cy.get('#swh-add-forge-tab') .should('have.class', 'active'); cy.get('#swh-add-forge-requests-list-tab') .should('not.have.class', 'active'); }); it('should show create form elements to authenticated user', function() { cy.userLogin(); cy.visit(this.addForgeNowUrl); cy.get('#swh-input-forge-type') .should('be.visible'); cy.get('#swh-input-forge-url') .should('be.visible'); cy.get('#swh-input-forge-contact-name') .should('be.visible'); cy.get('#swh-input-consent-check') .should('be.visible'); cy.get('#swh-input-forge-comment') .should('be.visible'); cy.get('#swh-input-form-submit') .should('be.visible'); }); it('should show browse requests table for every user', function() { // testing only for anonymous cy.visit(this.addForgeNowUrl); cy.get('#swh-add-forge-requests-list-tab').click(); cy.get('#add-forge-request-browse') .should('be.visible'); cy.get('#loginLink') .should('not.be.visible'); }); it('should update browse list on successful submission', function() { cy.userLogin(); cy.visit(this.addForgeNowUrl); populateForm('bitbucket', 'gitlab.com', 'test', 'test@example.com', 'on', 'test comment'); cy.get('#requestCreateForm').submit(); cy.visit(this.addForgeNowUrl); cy.get('#swh-add-forge-requests-list-tab').click(); cy.get('#add-forge-request-browse') .should('be.visible') .should('contain', 'gitlab.com'); cy.get('#add-forge-request-browse') .should('be.visible') .should('contain', 'PENDING'); }); it('should show error message on conflict', function() { cy.userLogin(); cy.visit(this.addForgeNowUrl); populateForm('bitbucket', 'gitlab.com', 'test', 'test@example.com', 'on', 'test comment'); cy.get('#requestCreateForm').submit(); cy.get('#requestCreateForm').submit(); // Submitting the same data again cy.get('#userMessage') .should('have.class', 'badge-danger') - .should('contain', 'Sorry; an error occurred'); + .should('contain', 'already exists'); }); + + it('should show error message', function() { + cy.userLogin(); + + cy.intercept('POST', `${this.Urls.api_1_add_forge_request_create()}**`, + { + body: { + 'exception': 'BadInputExc', + 'reason': '{"add-forge-comment": ["This field is required"]}' + }, + statusCode: 400 + }).as('errorRequest'); + + cy.visit(this.addForgeNowUrl); + + populateForm( + 'bitbucket', 'gitlab.com', 'test', 'test@example.com', 'on', 'comment' + ); + cy.get('#requestCreateForm').submit(); + + cy.wait('@errorRequest').then((xhr) => { + cy.get('#userMessage') + .should('have.class', 'badge-danger') + .should('contain', 'field is required'); + }); + }); + });