diff --git a/cypress/e2e/add-forge-now-request-create.cy.js b/cypress/e2e/add-forge-now-request-create.cy.js index e1414ff7..763e463f 100644 --- a/cypress/e2e/add-forge-now-request-create.cy.js +++ b/cypress/e2e/add-forge-now-request-create.cy.js @@ -1,258 +1,258 @@ /** * 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').clear().type(url, {delay: 0, force: true}); - cy.get('#swh-input-forge-contact-name').clear().type(contact, {delay: 0, force: true}); - cy.get('#swh-input-forge-contact-email').clear().type(email, {delay: 0, force: true}); + cy.get('#swh-input-forge-url').clear().type(url); + cy.get('#swh-input-forge-contact-name').clear().type(contact); + cy.get('#swh-input-forge-contact-email').clear().type(email); if (comment) { - cy.get('#swh-input-forge-comment').clear().type(comment, {delay: 0, force: true}); + cy.get('#swh-input-forge-comment').clear().type(comment); } cy.get('#swh-input-consent-check').click({force: consent === 'on'}); } describe('Browse requests list tests', function() { beforeEach(function() { this.addForgeNowUrl = this.Urls.forge_add_create(); this.listAddForgeRequestsUrl = this.Urls.add_forge_request_list_datatables(); }); it('should not show user requests filter checkbox for anonymous users', function() { cy.visit(this.addForgeNowUrl); cy.get('#swh-add-forge-requests-list-tab').click(); cy.get('#swh-add-forge-user-filter').should('not.exist'); }); it('should show user requests filter checkbox for authenticated users', function() { cy.userLogin(); cy.visit(this.addForgeNowUrl); cy.get('#swh-add-forge-requests-list-tab').click(); cy.get('#swh-add-forge-user-filter').should('exist').should('be.checked'); }); it('should only display user requests when filter is activated', function() { // Clean up previous state cy.task('db:add_forge_now:delete'); // 'user2' logs in and create requests cy.user2Login(); cy.visit(this.addForgeNowUrl); // create requests for the user 'user' populateForm('gitlab', 'gitlab.org', 'admin', 'admin@example.org', 'on', ''); cy.get('#requestCreateForm').submit(); // user requests filter checkbox should be in the DOM cy.get('#swh-add-forge-requests-list-tab').click(); cy.get('#swh-add-forge-user-filter').should('exist').should('be.checked'); // check unfiltered user requests cy.get('tbody tr').then(rows => { expect(rows.length).to.eq(1); }); // user1 logout cy.contains('a', 'logout').click(); // user logs in cy.userLogin(); cy.visit(this.addForgeNowUrl); populateForm('gitea', 'gitea.org', 'admin', 'admin@example.org', 'on', ''); cy.get('#requestCreateForm').submit(); populateForm('cgit', 'cgit.org', 'admin', 'admin@example.org', 'on', ''); cy.get('#requestCreateForm').submit(); // user requests filter checkbox should be in the DOM cy.get('#swh-add-forge-requests-list-tab').click(); cy.get('#swh-add-forge-user-filter').should('exist').should('be.checked'); // check unfiltered user requests cy.get('tbody tr').then(rows => { expect(rows.length).to.eq(2); }); cy.get('#swh-add-forge-user-filter') .uncheck({force: true}); // Users now sees everything cy.get('tbody tr').then(rows => { expect(rows.length).to.eq(2 + 1); }); }); }); describe('Test add-forge-request creation', function() { beforeEach(function() { this.addForgeNowUrl = this.Urls.forge_add_create(); }); it('should show all the 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'); cy.get('#swh-add-forge-requests-help-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_create()}`); }); 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-requests-help-tab') .should('not.have.class', 'active'); cy.url() .should('include', `${this.Urls.forge_add_list()}`); cy.get('#swh-add-forge-requests-help-tab').click(); cy.get('#swh-add-forge-tab') .should('not.have.class', 'active'); cy.get('#swh-add-forge-requests-list-tab') .should('not.have.class', 'active'); cy.get('#swh-add-forge-requests-help-tab') .should('have.class', 'active'); cy.url() .should('include', `${this.Urls.forge_add_help()}`); 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'); cy.get('#swh-add-forge-requests-help-tab') .should('not.have.class', 'active'); cy.url() .should('include', `${this.Urls.forge_add_create()}`); }); 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.exist'); }); 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', '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', 'off', 'comment' ); cy.get('#requestCreateForm').submit(); cy.wait('@errorRequest').then((xhr) => { cy.get('#userMessage') .should('have.class', 'badge-danger') .should('contain', 'field is required'); }); }); }); diff --git a/cypress/e2e/deposit-admin.cy.js b/cypress/e2e/deposit-admin.cy.js index a79daa4e..5b4c6890 100644 --- a/cypress/e2e/deposit-admin.cy.js +++ b/cypress/e2e/deposit-admin.cy.js @@ -1,239 +1,239 @@ /** * Copyright (C) 2020-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 */ // data to use as request query response let responseDeposits; let expectedOrigins; let depositModerationUrl; let depositListUrl; const $ = Cypress.$; describe('Test moderation deposit Login/logout', function() { before(function() { depositModerationUrl = this.Urls.admin_deposit(); }); it('should not display deposit moderation link in sidebar when anonymous', function() { cy.visit(depositModerationUrl); cy.get(`.sidebar a[href="${depositModerationUrl}"]`) .should('not.exist'); }); it('should not display deposit moderation link when connected as unprivileged user', function() { cy.userLogin(); cy.visit(depositModerationUrl); cy.get(`.sidebar a[href="${depositModerationUrl}"]`) .should('not.exist'); }); it('should display deposit moderation link in sidebar when connected as privileged user', function() { cy.depositLogin(); cy.visit(depositModerationUrl); cy.get(`.sidebar a[href="${depositModerationUrl}"]`) .should('exist'); }); it('should display deposit moderation link in sidebar when connected as staff member', function() { cy.adminLogin(); cy.visit(depositModerationUrl); cy.get(`.sidebar a[href="${depositModerationUrl}"]`) .should('exist'); }); }); describe('Test admin deposit page', function() { before(function() { depositModerationUrl = this.Urls.admin_deposit(); depositListUrl = this.Urls.admin_deposit_list(); }); beforeEach(() => { responseDeposits = [ { 'id': 614, 'type': 'code', 'external_id': 'ch-de-1', 'reception_date': '2020-05-18T13:48:27Z', 'status': 'done', 'status_detail': null, 'swhid': 'swh:1:dir:ef04a768', 'swhid_context': 'swh:1:dir:ef04a768;origin=https://w.s.o/c-d-1;visit=swh:1:snp:b234be1e;anchor=swh:1:rev:d24a75c9;path=/', 'raw_metadata': 'bar', 'uri': 'https://w.s.o/c-d-1' }, { 'id': 613, 'type': 'code', 'external_id': 'ch-de-2', 'reception_date': '2020-05-18T11:20:16Z', 'status': 'done', 'status_detail': null, 'swhid': 'swh:1:dir:181417fb', 'swhid_context': 'swh:1:dir:181417fb;origin=https://w.s.o/c-d-2;visit=swh:1:snp:8c32a2ef;anchor=swh:1:rev:3d1eba04;path=/', 'raw_metadata': null, 'uri': 'https://w.s.o/c-d-2' }, { 'id': 612, 'type': 'code', 'external_id': 'ch-de-3', 'reception_date': '2020-05-18T11:20:16Z', 'status': 'rejected', 'status_detail': 'incomplete deposit!', 'swhid': null, 'swhid_context': null, 'raw_metadata': null, 'uri': null } ]; // those are computed from the expectedOrigins = { 614: 'https://w.s.o/c-d-1', 613: 'https://w.s.o/c-d-2', 612: '' }; }); it('Should properly display entries', function() { cy.adminLogin(); const testDeposits = responseDeposits; cy.intercept(`${depositListUrl}**`, { body: { 'draw': 10, 'recordsTotal': testDeposits.length, 'recordsFiltered': testDeposits.length, 'data': testDeposits } }).as('listDeposits'); cy.visit(depositModerationUrl); cy.location('pathname') .should('be.equal', depositModerationUrl); cy.get('#swh-admin-deposit-list') .should('exist'); cy.wait('@listDeposits').then((xhr) => { cy.log('response:', xhr.response); cy.log(xhr.response.body); const deposits = xhr.response.body.data; cy.log('Deposits: ', deposits); expect(deposits.length).to.equal(testDeposits.length); cy.get('#swh-admin-deposit-list').find('tbody > tr').as('rows'); // only 2 entries cy.get('@rows').each((row, idx, collection) => { const cells = row[0].cells; const deposit = deposits[idx]; const responseDeposit = testDeposits[idx]; assert.isNotNull(deposit); assert.isNotNull(responseDeposit); expect(deposit.id).to.be.equal(responseDeposit['id']); expect(deposit.uri).to.be.equal(responseDeposit['uri']); expect(deposit.type).to.be.equal(responseDeposit['type']); expect(deposit.external_id).to.be.equal(responseDeposit['external_id']); expect(deposit.status).to.be.equal(responseDeposit['status']); expect(deposit.status_detail).to.be.equal(responseDeposit['status_detail']); expect(deposit.swhid).to.be.equal(responseDeposit['swhid']); expect(deposit.swhid_context).to.be.equal(responseDeposit['swhid_context']); const expectedOrigin = expectedOrigins[deposit.id]; // ensure it's in the dom cy.contains(deposit.id).should('be.visible'); if (deposit.status !== 'rejected') { expect(row).to.not.contain(deposit.external_id); cy.contains(expectedOrigin).should('be.visible'); } if (deposit.uri && deposit.swhid_context) { let html = `${deposit.uri}`; html += ` `; html += ''; expect($(cells[2]).html()).to.contain(html); } else if (!deposit.uri) { expect($(cells[2]).text().trim()).to.equal(''); } cy.contains(deposit.status).should('be.visible'); // those are hidden by default, so now visible if (deposit.status_detail !== null) { cy.contains(deposit.status_detail).should('not.exist'); } // those are hidden by default if (deposit.swhid !== null) { cy.contains(deposit.swhid).should('not.exist'); cy.contains(deposit.swhid_context).should('not.exist'); } if (deposit.raw_metadata !== null) { cy.get('button.metadata', {withinSubject: row}) .should('exist') .should('have.text', 'display') .click({force: true}); cy.get('#swh-web-modal-html code.xml').should('be.visible'); // Dismiss the modal - cy.get('body').wait(500).type('{esc}'); + cy.wait(500).get('#swh-web-modal-html .close').click(); cy.get('#swh-web-modal-html code.xml').should('not.be.visible'); } else { cy.get('button.metadata', {withinSubject: row}).should('not.exist'); cy.get('#swh-web-modal-html code.xml').should('not.be.visible'); } }); // toggling all links and ensure, the previous checks are inverted cy.get('a.toggle-col').click({'multiple': true}).then(() => { cy.get('#swh-admin-deposit-list').find('tbody > tr').as('rows'); cy.get('@rows').each((row, idx, collection) => { const deposit = deposits[idx]; const expectedOrigin = expectedOrigins[deposit.id]; // ensure it's in the dom expect(row).to.not.contain(deposit.id); if (deposit.status !== 'rejected') { expect(row).to.not.contain(deposit.external_id); expect(row).to.contain(expectedOrigin); } expect(row).to.not.contain(deposit.status); // those are hidden by default, so now visible if (deposit.status_detail !== null) { cy.contains(deposit.status_detail).should('be.visible'); } // those are hidden by default, so now they should be visible if (deposit.swhid !== null) { cy.contains(deposit.swhid).should('be.visible'); cy.contains(deposit.swhid_context).should('be.visible'); // check SWHID link text formatting cy.contains(deposit.swhid_context).then(elt => { expect(elt[0].innerHTML).to.equal(deposit.swhid_context.replace(/;/g, ';
')); }); } }); }); cy.get('#swh-admin-deposit-list-error') .should('not.contain', 'An error occurred while retrieving the list of deposits'); }); }); }); diff --git a/cypress/e2e/mailmap.cy.js b/cypress/e2e/mailmap.cy.js index d9ee1fde..10c350ab 100644 --- a/cypress/e2e/mailmap.cy.js +++ b/cypress/e2e/mailmap.cy.js @@ -1,226 +1,226 @@ /** * 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 */ const $ = Cypress.$; function fillFormAndSubmitMailmap(fromEmail, displayName, activated) { if (fromEmail) { cy.get('#swh-mailmap-from-email') .clear() - .type(fromEmail, {delay: 0, force: true}); + .type(fromEmail); } if (displayName) { cy.get('#swh-mailmap-display-name') .clear() - .type(displayName, {delay: 0, force: true}); + .type(displayName); } if (activated) { cy.get('#swh-mailmap-display-name-activated') .check({force: true}); } else { cy.get('#swh-mailmap-display-name-activated') .uncheck({force: true}); } cy.get('#swh-mailmap-form-submit') .click(); } function addNewMailmap(fromEmail, displayName, activated) { cy.get('#swh-add-new-mailmap') .click(); fillFormAndSubmitMailmap(fromEmail, displayName, activated); } function updateMailmap(fromEmail, displayName, activated) { cy.contains('Edit') .click(); fillFormAndSubmitMailmap(fromEmail, displayName, activated); } function checkMailmapRow(fromEmail, displayName, activated, processed = false, row = 1, nbRows = 1) { cy.get('tbody tr').then(rows => { assert.equal(rows.length, 1); const cells = rows[0].cells; assert.equal($(cells[0]).text(), fromEmail); assert.include($(cells[1]).html(), 'mdi-check-bold'); assert.equal($(cells[2]).text(), displayName); assert.include($(cells[3]).html(), activated ? 'mdi-check-bold' : 'mdi-close-thick'); assert.notEqual($(cells[4]).text(), ''); assert.include($(cells[5]).html(), processed ? 'mdi-check-bold' : 'mdi-close-thick'); }); } describe('Test mailmap administration', function() { before(function() { this.url = this.Urls.admin_mailmap(); }); beforeEach(function() { cy.task('db:user_mailmap:delete'); cy.intercept('POST', this.Urls.profile_mailmap_add()) .as('mailmapAdd'); cy.intercept('POST', this.Urls.profile_mailmap_update()) .as('mailmapUpdate'); cy.intercept(`${this.Urls.profile_mailmap_list_datatables()}**`) .as('mailmapList'); }); it('should not display mailmap admin link in sidebar when anonymous', function() { cy.visit(this.url); cy.get('.swh-mailmap-admin-item') .should('not.exist'); }); it('should not display mailmap admin link when connected as unprivileged user', function() { cy.userLogin(); cy.visit(this.url); cy.get('.swh-mailmap-admin-item') .should('not.exist'); }); it('should display mailmap admin link in sidebar when connected as privileged user', function() { cy.mailmapAdminLogin(); cy.visit(this.url); cy.get('.swh-mailmap-admin-item') .should('exist'); }); it('should not create a new mailmap when input data are empty', function() { cy.mailmapAdminLogin(); cy.visit(this.url); addNewMailmap('', '', true); cy.get('#swh-mailmap-form :invalid').should('exist'); cy.get('#swh-mailmap-form') .should('be.visible'); }); it('should not create a new mailmap when from email is invalid', function() { cy.mailmapAdminLogin(); cy.visit(this.url); addNewMailmap('invalid_email', 'display name', true); cy.get('#swh-mailmap-form :invalid').should('exist'); cy.get('#swh-mailmap-form') .should('be.visible'); }); it('should create a new mailmap when input data are valid', function() { cy.mailmapAdminLogin(); cy.visit(this.url); const fromEmail = 'user@example.org'; const displayName = 'New user display name'; addNewMailmap(fromEmail, displayName, true); cy.wait('@mailmapAdd'); cy.get('#swh-mailmap-form :invalid').should('not.exist'); // ensure table redraw before next check cy.contains(fromEmail); cy.get('#swh-mailmap-form') .should('not.be.visible'); checkMailmapRow(fromEmail, displayName, true); }); it('should not create a new mailmap for an email already mapped', function() { cy.mailmapAdminLogin(); cy.visit(this.url); const fromEmail = 'user@example.org'; const displayName = 'New user display name'; addNewMailmap(fromEmail, displayName, true); cy.wait('@mailmapAdd'); addNewMailmap(fromEmail, displayName, true); cy.wait('@mailmapAdd'); cy.get('#swh-mailmap-form') .should('not.be.visible'); cy.contains('Error') .should('be.visible'); checkMailmapRow(fromEmail, displayName, true); }); it('should update a mailmap', function() { cy.mailmapAdminLogin(); cy.visit(this.url); const fromEmail = 'user@example.org'; const displayName = 'New display name'; addNewMailmap(fromEmail, displayName, false); cy.wait('@mailmapAdd'); cy.get('#swh-mailmap-form :invalid').should('not.exist'); // ensure table redraw before next check cy.contains(fromEmail); cy.get('#swh-mailmap-form') .should('not.be.visible'); checkMailmapRow(fromEmail, displayName, false); const newDisplayName = 'Updated display name'; updateMailmap('', newDisplayName, true); cy.wait('@mailmapUpdate'); cy.get('#swh-mailmap-form :invalid').should('not.exist'); // ensure table redraw before next check cy.contains(fromEmail); cy.get('#swh-mailmap-form') .should('not.be.visible'); checkMailmapRow(fromEmail, newDisplayName, true); }); it('should indicate when a mailmap has been processed', function() { cy.mailmapAdminLogin(); cy.visit(this.url); const fromEmail = 'user@example.org'; const displayName = 'New user display name'; addNewMailmap(fromEmail, displayName, true); cy.wait('@mailmapAdd'); // ensure table redraw before next check cy.contains(fromEmail); checkMailmapRow(fromEmail, displayName, true, false); cy.task('db:user_mailmap:mark_processed'); cy.visit(this.url); checkMailmapRow(fromEmail, displayName, true, true); }); }); diff --git a/cypress/e2e/origin-search.cy.js b/cypress/e2e/origin-search.cy.js index 3e56ca82..d9934c96 100644 --- a/cypress/e2e/origin-search.cy.js +++ b/cypress/e2e/origin-search.cy.js @@ -1,653 +1,653 @@ /** * Copyright (C) 2019-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 */ const nonExistentText = 'NoMatchExists'; let origin; let url; function doSearch(searchText, searchInputElt = '#swh-origins-url-patterns') { if (searchText.startsWith('swh:')) { cy.intercept('**/api/1/resolve/**') .as('swhidResolve'); } cy.get(searchInputElt) // to avoid sending too much SWHID validation requests // as cypress insert character one by one when using type .invoke('val', searchText.slice(0, -1)) .type(searchText.slice(-1)) .get('.swh-search-icon') .click({force: true}); if (searchText.startsWith('swh:')) { cy.wait('@swhidResolve'); } } function searchShouldRedirect(searchText, redirectUrl) { doSearch(searchText); cy.location('pathname') .should('equal', redirectUrl); } function searchShouldShowNotFound(searchText, msg) { doSearch(searchText); if (searchText.startsWith('swh:')) { cy.get('.invalid-feedback') .should('be.visible') .and('contain', msg); } } function stubOriginVisitLatestRequests(status = 200, response = {type: 'tar'}, aliasSuffix = '') { cy.intercept({url: '**/visit/latest/**'}, { body: response, statusCode: status }).as(`originVisitLatest${aliasSuffix}`); } describe('Test origin-search', function() { before(function() { origin = this.origin[0]; url = this.Urls.browse_search(); }); beforeEach(function() { cy.visit(url); }); it('should have focus on search form after page load', function() { cy.get('#swh-origins-url-patterns') .should('have.attr', 'autofocus'); // for some reason, autofocus is not honored when running cypress tests // while it is in non controlled browsers // .should('have.focus'); }); it('should redirect to browse when archived URL is searched', function() { cy.get('#swh-origins-url-patterns') .type(origin.url); cy.get('.swh-search-icon') .click(); cy.location('pathname') .should('eq', this.Urls.browse_origin_directory()); cy.location('search') .should('eq', `?origin_url=${origin.url}`); }); it('should not redirect for non valid URL', function() { cy.get('#swh-origins-url-patterns') .type('www.example'); // Invalid URL cy.get('.swh-search-icon') .click(); cy.location('pathname') .should('eq', this.Urls.browse_search()); // Stay in the current page }); it('should not redirect for valid non archived URL', function() { cy.get('#swh-origins-url-patterns') .type('http://eaxmple.com/test/'); // Valid URL, but not archived cy.get('.swh-search-icon') .click(); cy.location('pathname') .should('eq', this.Urls.browse_search()); // Stay in the current page }); it('should remove origin URL with no archived content', function() { stubOriginVisitLatestRequests(404); // Using a non full origin URL here // This is because T3354 redirects to the origin in case of a valid, archived URL cy.get('#swh-origins-url-patterns') .type(origin.url.slice(0, -1)); cy.get('.swh-search-icon') .click(); cy.wait('@originVisitLatest'); cy.get('#origin-search-results') .should('be.visible') .find('tbody tr').should('have.length', 0); stubOriginVisitLatestRequests(200, {}, '2'); cy.get('.swh-search-icon') .click(); cy.wait('@originVisitLatest2'); cy.get('#origin-search-results') .should('be.visible') .find('tbody tr').should('have.length', 0); }); it('should filter origins by visit type', function() { cy.intercept('**/visit/latest/**').as('checkOriginVisits'); cy.get('#swh-origins-url-patterns') .type('http'); for (const visitType of ['git', 'tar']) { cy.get('#swh-search-visit-type') .select(visitType); cy.get('.swh-search-icon') .click(); cy.wait('@checkOriginVisits'); cy.get('#origin-search-results') .should('be.visible'); cy.get('tbody tr td.swh-origin-visit-type').then(elts => { for (const elt of elts) { cy.get(elt).should('have.text', visitType); } }); } }); it('should show not found message when no repo matches', function() { searchShouldShowNotFound(nonExistentText, 'No origins matching the search criteria were found.'); }); it('should add appropriate URL parameters', function() { // Check all three checkboxes and check if // correct url params are added cy.get('#swh-search-origins-with-visit') .check({force: true}) .get('#swh-filter-empty-visits') .check({force: true}) .get('#swh-search-origin-metadata') .check({force: true}) .then(() => { const searchText = origin.url.slice(0, -1); doSearch(searchText); cy.location('search').then(locationSearch => { const urlParams = new URLSearchParams(locationSearch); const query = urlParams.get('q'); const withVisit = urlParams.has('with_visit'); const withContent = urlParams.has('with_content'); const searchMetadata = urlParams.has('search_metadata'); assert.strictEqual(query, searchText); assert.strictEqual(withVisit, true); assert.strictEqual(withContent, true); assert.strictEqual(searchMetadata, true); }); }); }); it('should search in origin intrinsic metadata', function() { cy.intercept('GET', '**/origin/metadata-search/**').as( 'originMetadataSearch' ); cy.get('#swh-search-origins-with-visit') .check({force: true}) .get('#swh-filter-empty-visits') .check({force: true}) .get('#swh-search-origin-metadata') .check({force: true}) .then(() => { const searchText = 'plugin'; doSearch(searchText); cy.wait('@originMetadataSearch').then((req) => { expect(req.response.body[0].metadata.metadata.description).to.equal( 'Line numbering plugin for Highlight.js' // metadata is defined in _TEST_ORIGINS variable in swh/web/tests/data.py ); }); }); }); it('should not send request to the resolve endpoint', function() { cy.intercept(`${this.Urls.api_1_resolve_swhid('').slice(0, -1)}**`) .as('resolveSWHID'); cy.intercept(`${this.Urls.api_1_origin_search(origin.url.slice(0, -1))}**`) .as('searchOrigin'); cy.get('#swh-origins-url-patterns') - .type(origin.url.slice(0, -1), {delay: 0, force: true}); + .type(origin.url.slice(0, -1)); cy.get('.swh-search-icon') .click(); cy.wait('@searchOrigin'); cy.xhrShouldBeCalled('resolveSWHID', 0); cy.xhrShouldBeCalled('searchOrigin', 1); }); it('should add query language support for staff users', function() { cy.get('#swh-search-use-ql') .should('not.exist'); cy.adminLogin(); cy.visit(url); cy.get('#swh-search-use-ql') .should('exist'); }); it('should show error messages when using the query language', function() { cy.adminLogin(); cy.visit(url); cy.intercept('GET', `${this.Urls.api_1_origin_search('**')}**`, { body: { 'exception': 'BadInputExc', 'reason': 'Syntax error in search query: Invalid query' }, statusCode: 400 }) .as('searchOrigin'); cy.get('#swh-search-use-ql') .should('exist') .click({force: true}); // Covered by label cy.get('#swh-origins-url-patterns') .type('this is not a valid query') .type('{enter}'); cy.wait('@searchOrigin').then((xhr) => { cy.get('#swh-no-result') .should('contain', 'Syntax error in search query'); }); }); function checkSearchHasResults() { cy.get('.swh-search-icon') .click(); cy.wait('@checkOriginVisits'); cy.get('#origin-search-results') .should('be.visible'); cy.get('tbody tr td.swh-origin-visit-type') .should('exist'); } it('should search all origins when no pattern is provided', function() { cy.intercept('**/visit/latest/**').as('checkOriginVisits'); // with default filters checkSearchHasResults(); // remove filters cy.get('#swh-search-origins-with-visit') .uncheck({force: true}) .get('#swh-filter-empty-visits') .uncheck({force: true}); checkSearchHasResults(); }); it('should search all origins for a visit type', function() { cy.intercept('**/visit/latest/**').as('checkOriginVisits'); for (const visitType of ['git', 'tar']) { cy.get('#swh-search-visit-type') .select(visitType); checkSearchHasResults(); cy.get('tbody tr td.swh-origin-visit-type').then(elts => { for (const elt of elts) { cy.get(elt).should('have.text', visitType); } }); } }); context('Test pagination', function() { it('should not paginate if there are not many results', function() { // Setup search cy.get('#swh-search-origins-with-visit') .uncheck({force: true}) .get('#swh-filter-empty-visits') .uncheck({force: true}) .then(() => { const searchText = 'libtess'; // Get first page of results doSearch(searchText); cy.get('.swh-search-result-entry') .should('have.length', 1); cy.get('.swh-search-result-entry#origin-0 td a') .should('have.text', 'https://github.com/memononen/libtess2'); cy.get('#origins-prev-results-button') .should('have.class', 'disabled'); cy.get('#origins-next-results-button') .should('have.class', 'disabled'); }); }); it('should paginate forward when there are many results', function() { stubOriginVisitLatestRequests(); // Setup search cy.get('#swh-search-origins-with-visit') .uncheck({force: true}) .get('#swh-filter-empty-visits') .uncheck({force: true}) .then(() => { const searchText = 'many.origins'; // Get first page of results doSearch(searchText); cy.wait('@originVisitLatest'); cy.get('.swh-search-result-entry') .should('have.length', 100); cy.get('.swh-search-result-entry#origin-0 td a') .should('have.text', 'https://many.origins/1'); cy.get('.swh-search-result-entry#origin-99 td a') .should('have.text', 'https://many.origins/100'); cy.get('#origins-prev-results-button') .should('have.class', 'disabled'); cy.get('#origins-next-results-button') .should('not.have.class', 'disabled'); // Get second page of results cy.get('#origins-next-results-button a') .click(); cy.wait('@originVisitLatest'); cy.get('.swh-search-result-entry') .should('have.length', 100); cy.get('.swh-search-result-entry#origin-0 td a') .should('have.text', 'https://many.origins/101'); cy.get('.swh-search-result-entry#origin-99 td a') .should('have.text', 'https://many.origins/200'); cy.get('#origins-prev-results-button') .should('not.have.class', 'disabled'); cy.get('#origins-next-results-button') .should('not.have.class', 'disabled'); // Get third (and last) page of results cy.get('#origins-next-results-button a') .click(); cy.wait('@originVisitLatest'); cy.get('.swh-search-result-entry') .should('have.length', 50); cy.get('.swh-search-result-entry#origin-0 td a') .should('have.text', 'https://many.origins/201'); cy.get('.swh-search-result-entry#origin-49 td a') .should('have.text', 'https://many.origins/250'); cy.get('#origins-prev-results-button') .should('not.have.class', 'disabled'); cy.get('#origins-next-results-button') .should('have.class', 'disabled'); }); }); it('should paginate backward from a middle page', function() { stubOriginVisitLatestRequests(); // Setup search cy.get('#swh-search-origins-with-visit') .uncheck({force: true}) .get('#swh-filter-empty-visits') .uncheck({force: true}) .then(() => { const searchText = 'many.origins'; // Get first page of results doSearch(searchText); cy.wait('@originVisitLatest'); cy.get('#origins-prev-results-button') .should('have.class', 'disabled'); cy.get('#origins-next-results-button') .should('not.have.class', 'disabled'); // Get second page of results cy.get('#origins-next-results-button a') .click(); cy.wait('@originVisitLatest'); cy.get('#origins-prev-results-button') .should('not.have.class', 'disabled'); cy.get('#origins-next-results-button') .should('not.have.class', 'disabled'); // Get first page of results again cy.get('#origins-prev-results-button a') .click(); cy.wait('@originVisitLatest'); cy.get('.swh-search-result-entry') .should('have.length', 100); cy.get('.swh-search-result-entry#origin-0 td a') .should('have.text', 'https://many.origins/1'); cy.get('.swh-search-result-entry#origin-99 td a') .should('have.text', 'https://many.origins/100'); cy.get('#origins-prev-results-button') .should('have.class', 'disabled'); cy.get('#origins-next-results-button') .should('not.have.class', 'disabled'); }); }); it('should paginate backward from the last page', function() { stubOriginVisitLatestRequests(); // Setup search cy.get('#swh-search-origins-with-visit') .uncheck({force: true}) .get('#swh-filter-empty-visits') .uncheck({force: true}) .then(() => { const searchText = 'many.origins'; // Get first page of results doSearch(searchText); cy.wait('@originVisitLatest'); cy.get('#origins-prev-results-button') .should('have.class', 'disabled'); cy.get('#origins-next-results-button') .should('not.have.class', 'disabled'); // Get second page of results cy.get('#origins-next-results-button a') .click(); cy.wait('@originVisitLatest'); cy.get('#origins-prev-results-button') .should('not.have.class', 'disabled'); cy.get('#origins-next-results-button') .should('not.have.class', 'disabled'); // Get third (and last) page of results cy.get('#origins-next-results-button a') .click(); cy.get('#origins-prev-results-button') .should('not.have.class', 'disabled'); cy.get('#origins-next-results-button') .should('have.class', 'disabled'); // Get second page of results again cy.get('#origins-prev-results-button a') .click(); cy.wait('@originVisitLatest'); cy.get('.swh-search-result-entry') .should('have.length', 100); cy.get('.swh-search-result-entry#origin-0 td a') .should('have.text', 'https://many.origins/101'); cy.get('.swh-search-result-entry#origin-99 td a') .should('have.text', 'https://many.origins/200'); cy.get('#origins-prev-results-button') .should('not.have.class', 'disabled'); cy.get('#origins-next-results-button') .should('not.have.class', 'disabled'); // Get first page of results again cy.get('#origins-prev-results-button a') .click(); cy.wait('@originVisitLatest'); cy.get('.swh-search-result-entry') .should('have.length', 100); cy.get('.swh-search-result-entry#origin-0 td a') .should('have.text', 'https://many.origins/1'); cy.get('.swh-search-result-entry#origin-99 td a') .should('have.text', 'https://many.origins/100'); cy.get('#origins-prev-results-button') .should('have.class', 'disabled'); cy.get('#origins-next-results-button') .should('not.have.class', 'disabled'); }); }); }); context('Test valid SWHIDs', function() { it('should resolve directory', function() { const redirectUrl = this.Urls.browse_directory(origin.content[0].directory); const swhid = `swh:1:dir:${origin.content[0].directory}`; searchShouldRedirect(swhid, redirectUrl); }); it('should resolve revision', function() { const redirectUrl = this.Urls.browse_revision(origin.revisions[0]); const swhid = `swh:1:rev:${origin.revisions[0]}`; searchShouldRedirect(swhid, redirectUrl); }); it('should resolve snapshot', function() { const redirectUrl = this.Urls.browse_snapshot_directory(origin.snapshot); const swhid = `swh:1:snp:${origin.snapshot}`; searchShouldRedirect(swhid, redirectUrl); }); it('should resolve content', function() { const redirectUrl = this.Urls.browse_content(`sha1_git:${origin.content[0].sha1git}`); const swhid = `swh:1:cnt:${origin.content[0].sha1git}`; searchShouldRedirect(swhid, redirectUrl); }); it('should not send request to the search endpoint', function() { const swhid = `swh:1:rev:${origin.revisions[0]}`; cy.intercept(this.Urls.api_1_resolve_swhid(swhid)) .as('resolveSWHID'); cy.intercept(`${this.Urls.api_1_origin_search('').slice(0, -1)}**`) .as('searchOrigin'); cy.get('#swh-origins-url-patterns') - .type(swhid, {delay: 0, force: true}); + .type(swhid); cy.get('.swh-search-icon') .click(); cy.wait('@resolveSWHID'); cy.xhrShouldBeCalled('resolveSWHID', 1); cy.xhrShouldBeCalled('searchOrigin', 0); }); }); context('Test invalid SWHIDs', function() { it('should show not found for directory', function() { const swhid = `swh:1:dir:${this.unarchivedRepo.rootDirectory}`; const msg = `Directory with sha1_git ${this.unarchivedRepo.rootDirectory} not found`; searchShouldShowNotFound(swhid, msg); }); it('should show not found for snapshot', function() { const swhid = `swh:1:snp:${this.unarchivedRepo.snapshot}`; const msg = `Snapshot with id ${this.unarchivedRepo.snapshot} not found!`; searchShouldShowNotFound(swhid, msg); }); it('should show not found for revision', function() { const swhid = `swh:1:rev:${this.unarchivedRepo.revision}`; const msg = `Revision with sha1_git ${this.unarchivedRepo.revision} not found.`; searchShouldShowNotFound(swhid, msg); }); it('should show not found for content', function() { const swhid = `swh:1:cnt:${this.unarchivedRepo.content[0].sha1git}`; const msg = `Content with sha1_git checksum equals to ${this.unarchivedRepo.content[0].sha1git} not found!`; searchShouldShowNotFound(swhid, msg); }); function checkInvalidSWHIDReport(url, searchInputElt, swhidInput, validationMessagePattern = '') { cy.visit(url); doSearch(swhidInput, searchInputElt); cy.get(searchInputElt) .then($el => $el[0].checkValidity()).should('be.false'); cy.get(searchInputElt) .invoke('prop', 'validationMessage') .should('not.equal', '') .should('contain', validationMessagePattern); } it('should report invalid SWHID in search page input', function() { const swhidInput = `swh:1:cnt:${this.unarchivedRepo.content[0].sha1git};lines=45-60/`; checkInvalidSWHIDReport(this.Urls.browse_search(), '#swh-origins-url-patterns', swhidInput); cy.get('.invalid-feedback') .should('be.visible'); }); it('should report invalid SWHID in top right search input', function() { const swhidInput = `swh:1:cnt:${this.unarchivedRepo.content[0].sha1git};lines=45-60/`; checkInvalidSWHIDReport(this.Urls.browse_help(), '#swh-origins-search-top-input', swhidInput); }); it('should report SWHID with uppercase chars in search page input', function() { const swhidInput = `swh:1:cnt:${this.unarchivedRepo.content[0].sha1git}`.toUpperCase(); checkInvalidSWHIDReport(this.Urls.browse_search(), '#swh-origins-url-patterns', swhidInput, swhidInput.toLowerCase()); cy.get('.invalid-feedback') .should('be.visible'); }); it('should report SWHID with uppercase chars in top right search input', function() { let swhidInput = `swh:1:cnt:${this.unarchivedRepo.content[0].sha1git}`.toUpperCase(); swhidInput += ';lines=45-60/'; checkInvalidSWHIDReport(this.Urls.browse_help(), '#swh-origins-search-top-input', swhidInput.toLowerCase()); }); }); }); diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js index e703ec17..e6d417f8 100644 --- a/cypress/support/e2e.js +++ b/cypress/support/e2e.js @@ -1,110 +1,116 @@ /** * Copyright (C) 2019-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 'cypress-hmr-restarter'; import '@cypress/code-coverage/support'; Cypress.Screenshot.defaults({ screenshotOnRunFailure: false }); Cypress.Commands.add('xhrShouldBeCalled', (alias, timesCalled) => { const testRoutes = cy.state('routes'); const aliasRoute = Cypress._.find(testRoutes, {alias}); expect(Object.keys(aliasRoute.requests || {})).to.have.length(timesCalled); }); function loginUser(username, password) { const url = '/admin/login/'; return cy.request({ url: url, method: 'GET' }).then(() => { cy.getCookie('sessionid').should('not.exist'); cy.getCookie('csrftoken').its('value').then((token) => { cy.request({ url: url, method: 'POST', form: true, followRedirect: false, body: { username: username, password: password, csrfmiddlewaretoken: token } }).then(() => { cy.getCookie('sessionid').should('exist'); return cy.getCookie('csrftoken').its('value'); }); }); }); } Cypress.Commands.add('adminLogin', () => { return loginUser('admin', 'admin'); }); Cypress.Commands.add('userLogin', () => { return loginUser('user', 'user'); }); Cypress.Commands.add('user2Login', () => { return loginUser('user2', 'user2'); }); Cypress.Commands.add('ambassadorLogin', () => { return loginUser('ambassador', 'ambassador'); }); Cypress.Commands.add('depositLogin', () => { return loginUser('deposit', 'deposit'); }); Cypress.Commands.add('addForgeModeratorLogin', () => { return loginUser('add-forge-moderator', 'add-forge-moderator'); }); function mockCostlyRequests() { cy.intercept('https://status.softwareheritage.org/**', { body: { 'result': { 'status': [ { 'id': '5f7c4c567f50b304c1e7bd5f', 'name': 'Save Code Now', 'updated': '2020-11-30T13:51:21.151Z', 'status': 'Operational', 'status_code': 100 } ] } }}).as('swhPlatformStatus'); cy.intercept('/coverage', { body: '' }).as('swhCoverageWidget'); } Cypress.Commands.add('mailmapAdminLogin', () => { return loginUser('mailmap-admin', 'mailmap-admin'); }); before(function() { mockCostlyRequests(); cy.task('getSwhTestsData').then(testsData => { Object.assign(this, testsData); }); cy.visit('/').window().then(async win => { this.Urls = win.Urls; }); }); beforeEach(function() { mockCostlyRequests(); }); + +Cypress.Commands.overwrite('type', (originalFn, subject, text, options = {}) => { + options.delay = options.delay || 0; + options.force = options.force || true; + return originalFn(subject, text, options); +});