Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • olasd/swh-web
  • lunar/swh-web
  • jayeshv/swh-web
  • bchauvet/swh-web
  • ardumont/swh-web
  • anlambert/swh-web
  • vlorentz/swh-web
  • swh/devel/swh-web
  • KShivendu/swh-web
  • pabs/swh-web
  • douardda/swh-web
  • bmeles/swh-web
  • marmoute/swh-web
  • rboyer/swh-web
14 results
Show changes
Showing
with 1186 additions and 89 deletions
......@@ -21,7 +21,7 @@ describe('Test "Add Forge Now" moderation Login/logout', function() {
.type('admin')
.get('input[name="password"]')
.type('admin')
.get('.container form button[type=submit]')
.get('.container form #login-submit')
.click();
cy.location('pathname')
......@@ -43,7 +43,7 @@ describe('Test "Add Forge Now" moderation Login/logout', function() {
it('should not display moderation link in sidebar when anonymous', function() {
cy.visit(this.addForgeModerationUrl);
cy.get(`.sidebar a[href="${this.addForgeModerationUrl}"]`)
cy.get(`.app-sidebar a[href="${this.addForgeModerationUrl}"]`)
.should('not.exist');
});
......@@ -51,7 +51,7 @@ describe('Test "Add Forge Now" moderation Login/logout', function() {
cy.userLogin();
cy.visit(this.addForgeModerationUrl);
cy.get(`.sidebar a[href="${this.addForgeModerationUrl}"]`)
cy.get(`.app-sidebar a[href="${this.addForgeModerationUrl}"]`)
.should('not.exist');
});
......@@ -60,7 +60,7 @@ describe('Test "Add Forge Now" moderation Login/logout', function() {
cy.addForgeModeratorLogin();
cy.visit(this.addForgeModerationUrl);
cy.get(`.sidebar a[href="${this.addForgeModerationUrl}"]`)
cy.get(`.app-sidebar a[href="${this.addForgeModerationUrl}"]`)
.should('exist');
});
......@@ -68,7 +68,7 @@ describe('Test "Add Forge Now" moderation Login/logout', function() {
cy.adminLogin();
cy.visit(this.addForgeModerationUrl);
cy.get(`.sidebar a[href="${this.addForgeModerationUrl}"]`)
cy.get(`.app-sidebar a[href="${this.addForgeModerationUrl}"]`)
.should('exist');
});
});
......
......@@ -23,7 +23,7 @@ describe('Test Admin Login/logout', function() {
.type('admin')
.get('input[name="password"]')
.type('admin')
.get('.container form button[type=submit]')
.get('.container form #login-submit')
.click();
cy.location('pathname')
......@@ -36,10 +36,10 @@ describe('Test Admin Login/logout', function() {
cy.adminLogin();
cy.visit(this.url);
cy.get(`.sidebar a[href="${this.Urls.admin_origin_save_requests()}"]`)
cy.get(`.app-sidebar a[href="${this.Urls.admin_origin_save_requests()}"]`)
.should('be.visible');
cy.get(`.sidebar a[href="${this.Urls.admin_deposit()}"]`)
cy.get(`.app-sidebar a[href="${this.Urls.admin_deposit()}"]`)
.should('be.visible');
logout();
......@@ -226,8 +226,9 @@ describe('Test Admin Origin Save', function() {
cy.get('#swh-save-requests-rejected-tab')
.click();
cy.contains('#swh-origin-save-rejected-requests', encodeURI(originUrl))
.click();
cy.contains('#swh-origin-save-rejected-requests tr', encodeURI(originUrl))
.click()
.should('have.class', 'selected');
cy.get('#swh-remove-rejected-save-origin-request')
.click();
......@@ -257,8 +258,9 @@ describe('Test Admin Origin Save', function() {
cy.visit(this.Urls.admin_origin_save_requests());
// admin rejects the save request and adds a rejection note
cy.contains('#swh-origin-save-pending-requests', originUrl)
.click();
cy.contains('#swh-origin-save-pending-requests tr', originUrl)
.click()
.should('have.class', 'selected');
cy.get('#swh-reject-save-origin-request')
.click();
......@@ -323,8 +325,9 @@ describe('Test Admin Origin Save', function() {
cy.visit(this.Urls.admin_origin_save_requests());
// admin rejects the save request
cy.contains('#swh-origin-save-pending-requests', encodeURI(originUrl))
.click();
cy.contains('#swh-origin-save-pending-requests tr', encodeURI(originUrl))
.click()
.should('have.class', 'selected');
cy.get('#swh-reject-save-origin-request')
.click();
......
This diff is collapsed.
......@@ -8,7 +8,12 @@
describe('Test API tokens UI', function() {
it('should ask for user to login', function() {
cy.visit(`${this.Urls.oidc_profile()}#tokens`, {failOnStatusCode: false});
// mock keycloak
cy.intercept(`${this.Urls.oidc_login()}/**`, {
headers: {'content-type': 'text/html'},
body: {}
});
cy.visit(`${this.Urls.oidc_profile()}#tokens`);
cy.location().should(loc => {
expect(loc.pathname).to.eq(this.Urls.oidc_login());
});
......@@ -96,7 +101,7 @@ describe('Test API tokens UI', function() {
const errorMessage = 'Bearer token has expired';
displayToken(this.Urls, 400, errorMessage);
cy.get('.modal-body')
.should('contain', errorMessage);
.should('contain', errorMessage);
});
function revokeToken(Urls, status) {
......
/**
* Copyright (C) 2020-2022 The Software Heritage developers
* Copyright (C) 2020-2024 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
......@@ -20,7 +20,7 @@ describe('Test moderation deposit Login/logout', function() {
it('should not display deposit moderation link in sidebar when anonymous', function() {
cy.visit(depositModerationUrl);
cy.get(`.sidebar a[href="${depositModerationUrl}"]`)
cy.get(`.app-sidebar a[href="${depositModerationUrl}"]`)
.should('not.exist');
});
......@@ -28,7 +28,7 @@ describe('Test moderation deposit Login/logout', function() {
cy.userLogin();
cy.visit(depositModerationUrl);
cy.get(`.sidebar a[href="${depositModerationUrl}"]`)
cy.get(`.app-sidebar a[href="${depositModerationUrl}"]`)
.should('not.exist');
});
......@@ -37,7 +37,7 @@ describe('Test moderation deposit Login/logout', function() {
cy.depositLogin();
cy.visit(depositModerationUrl);
cy.get(`.sidebar a[href="${depositModerationUrl}"]`)
cy.get(`.app-sidebar a[href="${depositModerationUrl}"]`)
.should('exist');
});
......@@ -45,7 +45,7 @@ describe('Test moderation deposit Login/logout', function() {
cy.adminLogin();
cy.visit(depositModerationUrl);
cy.get(`.sidebar a[href="${depositModerationUrl}"]`)
cy.get(`.app-sidebar a[href="${depositModerationUrl}"]`)
.should('exist');
});
......@@ -188,7 +188,7 @@ describe('Test admin deposit page', function() {
cy.get('#swh-web-modal-html code.xml').should('be.visible');
// Dismiss the modal
cy.wait(500).get('#swh-web-modal-html .close').click();
cy.wait(500).get('#swh-web-modal-html .btn-close').click();
cy.get('#swh-web-modal-html code.xml').should('not.be.visible');
} else {
cy.get('button.metadata', {withinSubject: row}).should('not.exist');
......
/**
* Copyright (C) 2019-2021 The Software Heritage developers
* Copyright (C) 2019-2024 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
......@@ -103,7 +103,11 @@ describe('Home Page Tests', function() {
cy.location('search')
.should('equal', `?q=${searchText}&with_visit=true&with_content=true`);
});
it('should have anchors to headings', function() {
cy.visit(url);
cy.get('.swh-heading-anchor').should('exist');
});
});
......@@ -6,7 +6,8 @@
*/
const url = '/browse/help/';
const statusUrl = 'https://status.softwareheritage.org';
// statusUrl must match what is configured in settings/test.py
const statusUrl = 'https://status.example.org';
describe('Test top-bar', function() {
beforeEach(function() {
......@@ -192,7 +193,7 @@ describe('Test navbar', function() {
cy.get('#swh-origins-search-top-input')
.type(keyword);
cy.get('#swh-origins-search-top button[type=submit]')
cy.get('#swh-origins-search-top #swh-origins-search-submit')
.click({force: true});
cy.url()
......
......@@ -72,8 +72,6 @@ describe('Test mailmap administration', function() {
.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() {
......@@ -195,13 +193,9 @@ describe('Test mailmap administration', function() {
cy.get('#swh-mailmap-form :invalid').should('not.exist');
// ensure table redraw before next check
cy.contains(fromEmail).should('be.visible');
cy.get('#swh-mailmap-form')
.should('not.be.visible');
cy.contains(newDisplayName).should('be.visible');
checkMailmapRow(fromEmail, newDisplayName, true);
});
it('should indicate when a mailmap has been processed', function() {
......
......@@ -22,15 +22,15 @@ const saveCodeMsg = {
'csrfError': 'CSRF Failed: Referrer checking failed - no Referrer.'
};
const anonymousVisitTypes = ['bzr', 'cvs', 'git', 'hg', 'svn'];
const allVisitTypes = ['archives', 'bzr', 'cvs', 'git', 'hg', 'svn'];
const anonymousVisitTypes = ['bzr', 'cvs', 'git', 'hg', 'svn', 'tarball'];
const allVisitTypes = ['archives', 'bzr', 'cvs', 'git', 'hg', 'svn', 'tarball'];
function makeOriginSaveRequest(originType, originUrl) {
cy.get('#swh-input-origin-url')
.type(originUrl)
.get('#swh-input-visit-type')
.select(originType)
.get('#swh-save-origin-form button[type=submit]')
.get('#swh-save-origin-form #swh-input-origin-save-submit')
.click();
}
......@@ -430,7 +430,7 @@ describe('Origin Save Tests', function() {
.should('not.exist');
});
it('should hide task info popover when clicking on the close button', function() {
it('should hide task info popover when clicking outside it', function() {
cy.intercept('/save/requests/list/**', {fixture: 'origin-save'})
.as('saveRequestsList');
cy.intercept('/save/task/info/**', {fixture: 'save-task-info'})
......@@ -446,7 +446,7 @@ describe('Origin Save Tests', function() {
cy.get('.swh-save-request-info-popover')
.should('be.visible');
cy.get('.swh-save-request-info-close')
cy.get('#dt-search-0')
.click();
cy.get('.swh-save-request-info-popover')
......@@ -650,7 +650,7 @@ describe('Origin Save Tests', function() {
.get('#swh-input-artifact-version-0')
.clear()
.type(artifactVersion)
.get('#swh-save-origin-form button[type=submit]')
.get('#swh-save-origin-form #swh-input-origin-save-submit')
.click();
cy.wait('@saveRequest').then(() => {
......@@ -728,7 +728,7 @@ describe('Origin Save Tests', function() {
}).as('saveRequest');
// submit form
cy.get('#swh-save-origin-form button[type=submit]')
cy.get('#swh-save-origin-form #swh-input-origin-save-submit')
.click();
// submission should be successful
......@@ -808,7 +808,7 @@ describe('Origin Save Tests', function() {
.type(originUrl);
// submit form
cy.get('#swh-save-origin-form button[type=submit]')
cy.get('#swh-save-origin-form #swh-input-origin-save-submit')
.click();
// submission should be successful
......@@ -844,7 +844,7 @@ describe('Origin Save Tests', function() {
it('should not accept origin URL with password', function() {
makeOriginSaveRequest('git', 'https://user:password@git.example.org/user/repo');
makeOriginSaveRequest('git', 'https://user:pass@git.example.org/user/repo');
cy.get('.invalid-feedback')
.should('contain', 'The origin url contains a password and cannot be accepted for security reasons');
......@@ -891,6 +891,26 @@ describe('Origin Save Tests', function() {
});
it('should accept origin URL with anonymous/password credentials', function() {
cy.adminLogin();
cy.visit(url);
const originUrl = 'https://anonymous:password@git.example.org/user/repo';
stubSaveRequest({requestUrl: this.Urls.api_1_save_origin('git', originUrl),
saveRequestStatus: 'accepted',
originUrl: originUrl,
saveTaskStatus: 'pending'});
makeOriginSaveRequest('git', originUrl);
cy.wait('@saveRequest').then(() => {
checkAlertVisible('success', saveCodeMsg['success']);
});
});
it('should accept origin URL with empty password', function() {
cy.adminLogin();
......
/**
* Copyright (C) 2019-2024 The Software Heritage developers
* Copyright (C) 2019-2025 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
......@@ -15,6 +15,9 @@ function doSearch(searchText, searchInputElt = '#swh-origins-url-patterns') {
if (searchText.startsWith('swh:')) {
cy.intercept('**/api/1/resolve/**')
.as('swhidResolve');
} else {
cy.intercept('**/visit/latest/**')
.as('checkOriginVisits');
}
cy.get(searchInputElt)
// to avoid sending too much SWHID validation requests
......@@ -25,6 +28,8 @@ function doSearch(searchText, searchInputElt = '#swh-origins-url-patterns') {
.click({force: true});
if (searchText.startsWith('swh:')) {
cy.wait('@swhidResolve');
} else if (searchText.startsWith('http')) {
cy.wait('@checkOriginVisits');
}
}
......@@ -100,6 +105,17 @@ describe('Test origin-search', function() {
.should('eq', this.Urls.browse_search()); // Stay in the current page
});
it('should not redirect for an origin without any snapshots', function() {
cy.get('#swh-origins-url-patterns')
// valid origin URL but with null snapshot
.type('https://example.org/project/with/null/snapshot');
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);
......@@ -115,18 +131,6 @@ describe('Test origin-search', function() {
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() {
......@@ -146,6 +150,8 @@ describe('Test origin-search', function() {
cy.get('#origin-search-results')
.should('be.visible');
cy.wait('@checkOriginVisits');
cy.get('tbody tr td.swh-origin-visit-type').then(elts => {
for (const elt of elts) {
cy.get(elt).should('have.text', visitType);
......
/**
* Copyright (C) 2019-2020 The Software Heritage developers
* Copyright (C) 2019-2024 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
......@@ -45,11 +45,11 @@ describe('Test Revision View', function() {
it('should add/remove #swh-revision-changes url fragment when switching tab', function() {
const url = this.Urls.browse_revision(revision) + `?origin=${origin}`;
cy.visit(url);
cy.get('a[data-toggle="tab"]')
cy.get('a[data-bs-toggle="tab"]')
.contains('Changes')
.click();
cy.hash().should('be.equal', '#swh-revision-changes');
cy.get('a[data-toggle="tab"]')
cy.get('a[data-bs-toggle="tab"]')
.contains('Files')
.click();
cy.hash().should('be.equal', '');
......@@ -75,7 +75,7 @@ describe('Test Diffs View', function() {
diffData = res.body;
});
});
cy.get('a[data-toggle="tab"]')
cy.get('a[data-bs-toggle="tab"]')
.contains('Changes')
.click();
});
......@@ -477,7 +477,7 @@ describe('Test Diffs View', function() {
it('should highlight diff lines properly when a content is browsed in the Files tab', function() {
const url = this.Urls.browse_revision(revision) + `?origin=${origin}&path=README.md`;
cy.visit(url);
cy.get('a[data-toggle="tab"]')
cy.get('a[data-bs-toggle="tab"]')
.contains('Changes')
.click();
const diffHighlightingData = diffsHighlightingData['unified'];
......
/**
* Copyright (C) 2019 The Software Heritage developers
* Copyright (C) 2019-2024 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
......@@ -24,7 +24,7 @@ describe('Sidebar tests On Large Screen', function() {
.should('have.class', 'sidebar-collapse')
.get('.nav-link > p')
.should('not.be.visible');
cy.get('.sidebar .nav-header')
cy.get('.app-sidebar .nav-header')
.should('have.css', 'display', 'none');
cy.get('.swh-push-menu')
.should('have.attr', 'aria-expanded', 'false')
......@@ -33,7 +33,7 @@ describe('Sidebar tests On Large Screen', function() {
.should('not.have.class', 'sidebar-collapse')
.get('.nav-link > p')
.should('be.visible');
cy.get('.sidebar .nav-header')
cy.get('.app-sidebar .nav-header')
.should('not.have.css', 'display', 'none');
cy.get('.swh-push-menu')
.should('have.attr', 'aria-expanded', 'true');
......@@ -50,7 +50,7 @@ describe('Sidebar tests On Large Screen', function() {
})
.get('.swh-push-menu')
.click()
.get('.swh-sidebar-expanded')
.get('.sidebar-open')
.invoke('width')
.then(openWidth => {
assert.isBelow(collapseWidth, openWidth);
......@@ -74,7 +74,7 @@ describe('Sidebar Tests on small screens', function() {
.click()
.get('.swh-sidebar')
.should('be.visible')
.get('#sidebar-overlay')
.get('.sidebar-overlay')
.click({force: true})
.get('.swh-sidebar')
.should('not.be.visible');
......
/**
* Copyright (C) 2019-2020 The Software Heritage developers
* Copyright (C) 2019-2024 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
......@@ -98,7 +98,7 @@ describe('SWHIDs Tests', function() {
cy.get('#swh-identifiers-content')
.should('have.css', 'display', 'none');
cy.get('.ui-slideouttab-handle')
cy.get('#swh-identifiers .ui-slideouttab-handle')
.click();
cy.get('#swh-identifiers')
......@@ -106,7 +106,7 @@ describe('SWHIDs Tests', function() {
cy.get('#swh-identifiers-content')
.should('have.css', 'display', 'block');
cy.get('.ui-slideouttab-handle')
cy.get('#swh-identifiers .ui-slideouttab-handle')
.click();
cy.get('#swh-identifiers')
......@@ -117,7 +117,7 @@ describe('SWHIDs Tests', function() {
});
it('should display identifiers with permalinks for browsed objects', function() {
cy.get('.ui-slideouttab-handle')
cy.get('#swh-identifiers .ui-slideouttab-handle')
.click();
for (const td of testsData) {
......@@ -136,7 +136,7 @@ describe('SWHIDs Tests', function() {
});
it('should update other object identifiers contextual info when toggling context checkbox', function() {
cy.get('.ui-slideouttab-handle')
cy.get('#swh-identifiers .ui-slideouttab-handle')
.click();
for (const td of testsData) {
......@@ -166,7 +166,7 @@ describe('SWHIDs Tests', function() {
});
it('should display swh badges in identifiers tab for browsed objects', function() {
cy.get('.ui-slideouttab-handle')
cy.get('#swh-identifiers .ui-slideouttab-handle')
.click();
const originBadgeUrl = this.Urls.swh_badge('origin', origin.url);
......@@ -187,7 +187,7 @@ describe('SWHIDs Tests', function() {
it('should display badge integration info when clicking on it', function() {
cy.get('.ui-slideouttab-handle')
cy.get('#swh-identifiers .ui-slideouttab-handle')
.click();
for (const td of testsData) {
......@@ -207,7 +207,7 @@ describe('SWHIDs Tests', function() {
cy.get('.modal .swh-badge-html')
.should('contain.text', `alt="Archived | ${origin.url}"`);
cy.get('.modal.show .close')
cy.get('.modal.show .btn-close')
.click()
.wait(500);
......@@ -224,7 +224,7 @@ describe('SWHIDs Tests', function() {
cy.get('.modal .swh-badge-html')
.should('contain.text', `alt="Archived | ${td.objectSWHIDs[1]}"`);
cy.get('.modal.show .close')
cy.get('.modal.show .btn-close')
.click()
.wait(500);
......@@ -244,9 +244,9 @@ describe('SWHIDs Tests', function() {
it('should update tab size according to screen size', function() {
// use a small viewport size
cy.viewport(320, 480);
cy.viewport(800, 400);
cy.visit(url);
cy.get('.ui-slideouttab-handle')
cy.get('#swh-identifiers .ui-slideouttab-handle')
.click();
cy.window().then(win => {
// SWHIDs tab should fit in the screen
......@@ -259,4 +259,210 @@ describe('SWHIDs Tests', function() {
});
});
it('should update copy buttons after clicking on them', function() {
cy.get('#swh-identifiers .ui-slideouttab-handle')
.click();
for (const td of testsData) {
cy.get(`a[href="#swhid-tab-${td.objectType}"]`)
.click();
cy.get(`#swhid-tab-${td.objectType}`)
.should('be.visible');
cy.get(`#swhid-tab-${td.objectType} .btn-swhid-copy`)
.should('contain.text', 'Copy identifier')
.should('not.contain.text', 'Copied!')
.click()
.should('have.text', 'Copied!')
.wait(1001)
.should('not.contain.text', 'Copied!')
.should('contain.text', 'Copy identifier');
cy.get(`#swhid-tab-${td.objectType} .btn-swhid-url-copy`)
.should('contain.text', 'Copy permalink')
.should('not.contain.text', 'Copied!')
.click()
.should('have.text', 'Copied!')
.wait(1001)
.should('not.contain.text', 'Copied!')
.should('contain.text', 'Copy permalink');
}
});
});
function logout() {
cy.contains('logout')
.click();
}
describe('Citations Tests', function() {
beforeEach(function() {
const originUrl = 'https://git.example.org/repo_with_cff_file';
this.url = `${this.Urls.browse_origin()}?origin_url=${originUrl}`;
cy.adminLogin();
cy.visit(this.url);
cy.intercept(this.Urls.api_1_raw_intrinsic_citation_swhid_get() + '**')
.as('apiRawIntrinsicCitationGet');
});
it('should not make citations tab available for anonymous user', function() {
logout();
cy.visit(this.url);
cy.get('#swh-citations .ui-slideouttab-handle')
.should('not.exist');
});
it('should make citations tab available for ambassador user', function() {
logout();
cy.ambassadorLogin();
cy.visit(this.url);
cy.get('#swh-citations .ui-slideouttab-handle')
.should('be.visible');
});
it('should make citations tab current when clicking on its handle', function() {
cy.get('#swh-citations .ui-slideouttab-handle')
.should('be.visible')
.click();
cy.get('#swh-citations-content')
.should('be.visible');
cy.get('#swh-identifiers-content')
.should('not.be.visible');
});
it('should switch opened tabs when clicking on their handles', function() {
cy.get('#swh-identifiers .ui-slideouttab-handle')
.click();
cy.get('#swh-identifiers-content')
.should('be.visible');
cy.get('#swh-citations-content')
.should('not.be.visible');
cy.get('#swh-citations .ui-slideouttab-handle')
.click();
cy.get('#swh-citations-content')
.should('be.visible');
cy.get('#swh-identifiers-content')
.should('not.be.visible');
cy.get('#swh-identifiers .ui-slideouttab-handle')
.click();
cy.get('#swh-identifiers-content')
.should('be.visible');
cy.get('#swh-citations-content')
.should('not.be.visible');
});
it('should generate BibTex citation when selecting an object type', function() {
cy.get('#swh-citations .ui-slideouttab-handle')
.click();
cy.wait('@apiRawIntrinsicCitationGet');
// citation for directory object type is generated when opening citations tab
cy.get('#citation-tab-directory .swh-citation')
.should('contain', '@softwareversion{');
const citationMetadataSWHID = 'swh:1:cnt:a93d22c5d806cd945de928e60e93b52b141c653e;' +
'origin=https://git.example.org/repo_with_cff_file;' +
'visit=swh:1:snp:eefa4d83d6ffb2b9b293bc3af1345a298d56af54;' +
'anchor=swh:1:rev:384d62ee00f4cb3d4fa9f20a342c6f7209c9efe1;' +
'path=/citation.cff';
cy.get('#citation-source-directory a')
.should('exist')
.should('have.attr', 'href', `/${citationMetadataSWHID}`);
cy.get('#citation-copy-button-directory .btn-citation-copy')
.should('be.enabled');
// revision object type
cy.get(`a[href="#citation-tab-revision"]`)
.click();
cy.wait('@apiRawIntrinsicCitationGet');
cy.get('#citation-tab-revision .swh-citation')
.should('contain', '@softwareversion{');
cy.get('#citation-source-revision a')
.should('exist')
.should('have.attr', 'href', `/${citationMetadataSWHID}`);
cy.get('#citation-copy-button-revision .btn-citation-copy')
.should('be.enabled');
// snapshot object type
cy.get(`a[href="#citation-tab-snapshot"]`)
.click();
cy.wait('@apiRawIntrinsicCitationGet');
cy.get('#citation-tab-snapshot .swh-citation')
.should('contain', '@software{');
cy.get('#citation-source-snapshot a')
.should('exist')
.should('have.attr', 'href', `/${citationMetadataSWHID}`);
cy.get('#citation-copy-button-snapshot .btn-citation-copy')
.should('be.enabled');
});
it('should copy BibTex citation to clipboard', function() {
cy.get('#swh-citations .ui-slideouttab-handle')
.click();
cy.wait('@apiRawIntrinsicCitationGet');
cy.get('#citation-copy-button-directory .btn-citation-copy')
.click();
cy.window().then(win => {
win.navigator.clipboard.readText().then(text => {
expect(text.startsWith('@softwareversion{')).to.be.true;
});
});
});
it('should add selected lines info when generating citation for content', function() {
cy.get('td a')
.contains('citation.cff')
.click();
cy.get('.hljs-ln-numbers[data-line-number="1"]')
.click()
.get('.hljs-ln-numbers[data-line-number="3"]')
.click({shiftKey: true});
cy.get('#swh-citations .ui-slideouttab-handle')
.click();
cy.wait('@apiRawIntrinsicCitationGet');
cy.get('#citation-tab-content .swh-citation')
.should('contain', ';lines=1-3');
});
it('should not display citations tab if no citation can be generated', function() {
cy.visit(`${this.Urls.browse_origin_directory()}?origin_url=${this.origin[0].url}`);
cy.wait('@apiRawIntrinsicCitationGet');
cy.get('#swh-citations')
.should('not.be.visible');
});
});
......@@ -212,6 +212,14 @@ describe('Vault Cooking User Interface Tests', function() {
updateVaultItemList(this.vaultItems);
// Stub response to the vault API to simulate archive download
cy.intercept({url: this.vaultDownloadRevisionUrl}, {
body: {},
headers: {
'Content-Type': 'json'
}
}).as('fetchCookedArchive');
cy.visit(this.Urls.vault());
cy.contains(`#vault-task-${CSS.escape(this.revision)} button`, 'Download')
......@@ -517,6 +525,54 @@ describe('Vault Cooking User Interface Tests', function() {
});
});
it('should offer to recook a tarball already cooked by another user but no longer in cache', function() {
cy.visit(this.directoryUrl);
// set bundle as already cooked
cy.intercept(this.vaultDirectoryUrl, {
body: this.genVaultDirCookingResponse('done')
}).as('checkVaultCookingTask');
// but no longer available in cache
cy.intercept({url: this.vaultDownloadDirectoryUrl}, {
statusCode: 404,
body: {
'exception': 'NotFoundExc',
'reason': `Directory with ID '${this.directory}' not found.`
},
headers: {
'Content-Type': 'json'
}
}).as('fetchCookedArchive');
cy.intercept('POST', this.vaultDirectoryUrl, {
body: this.genVaultDirCookingResponse('new')
}).as('createVaultCookingTask');
// request tarball download
cy.contains('button', 'Download')
.click();
cy.wait('@checkVaultCookingTask');
// vault backend indicated tarball was already cooked, download dialog is displayed
cy.get('#vault-download-directory-modal')
.should('be.visible')
.contains('button:visible', 'Ok')
.click();
cy.wait('@fetchCookedArchive');
// tarball is no longer in cache, recook dialog is displayed
cy.get('#vault-recook-object-modal > .modal-dialog')
.should('be.visible')
.contains('button:visible', 'Ok')
.click();
// check new cooking request was sent
cy.wait('@createVaultCookingTask');
});
it('should remove selected vault items', function() {
updateVaultItemList(this.vaultItems);
......
......@@ -8,9 +8,12 @@
const axios = require('axios');
const {execFileSync} = require('child_process');
const fs = require('fs');
const path = require('path');
const sqlite3 = require('sqlite3').verbose();
const cypressSplit = require('cypress-split');
const emailPath = '/tmp/swh/mails';
let buildId = process.env.CYPRESS_PARALLEL_BUILD_ID;
if (buildId === undefined) {
buildId = '';
......@@ -44,7 +47,7 @@ function getDatabase() {
const db = new sqlite3.Database(`./swh-web-test${buildId}.sqlite3`);
// to prevent "database is locked" error when running tests
db.configure('busyTimeout', 20000);
db.run('PRAGMA journal_mode = WAL;');
db.exec('PRAGMA journal_mode = WAL;');
return db;
}
......@@ -175,13 +178,44 @@ module.exports = (on, config) => {
processAddForgeNowInboundEmail(emailSrc) {
try {
execFileSync('django-admin',
['process_inbound_email', '--settings=swh.web.settings.tests'],
['process_inbound_email', '--settings=swh.web.settings.cypress'],
{input: emailSrc});
return true;
} catch (_) {
return false;
}
},
/**
* Finds the first email in `emailPath` matching filters
* @param {*} param0 an object containing mail filters: subject, recipient
* @returns {string} the first matching mail content
*/
findEmail({subject, recipient}) {
for (const fileName of fs.readdirSync(emailPath)) {
// if multiple emails are sent by django at the same time they are stored in
// a single file separated by 80 dashes
const messages = fs.readFileSync(path.join(emailPath, fileName), 'utf8');
for (const message of messages.split('-'.repeat(79))) {
if (message.includes(`Subject: ${subject}`) && message.includes(`To: ${recipient}`)) {
return message;
}
}
}
},
/**
* Deletes all files found in `emailPath`
* @returns {bool} true
*/
cleanupEmails() {
if (!fs.existsSync(emailPath)) {
fs.mkdirSync(emailPath, {recursive: true});
return true;
}
fs.readdirSync(emailPath).forEach((fileName) => {
fs.unlinkSync(path.join(emailPath, fileName));
});
return true;
},
accessibilityChecker: require('cypress-accessibility-checker/plugin')
});
return config;
......
/**
* Copyright (C) 2019-2023 The Software Heritage developers
* Copyright (C) 2019-2024 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
......@@ -70,6 +70,10 @@ Cypress.Commands.add('addForgeModeratorLogin', () => {
return loginUser('add-forge-moderator', 'add-forge-moderator');
});
Cypress.Commands.add('alterSupportLogin', () => {
return loginUser('alter-support', 'alter-support');
});
function mockCostlyRequests() {
cy.intercept('https://status.softwareheritage.org/**', {
body: {
......@@ -113,6 +117,17 @@ before(function() {
beforeEach(function() {
mockCostlyRequests();
// intercept xhr failures (e.g. failed internal API calls)
cy.intercept('**', request => {
request.on('response', function(response) {
// expect(response.statusCode).is.lessThan(500); is a bit too verbose
const statusCode = response.statusCode;
const url = request.url;
if (statusCode >= 500) {
throw new Error(`${url} returned a ${statusCode} http status code`);
}
});
});
});
Cypress.Commands.overwrite('type', (originalFn, subject, text, options = {}) => {
......
include Makefile.sphinx
APIDOC_EXCLUDES += ../assets ../cypress ../static ../swh/*/settings
APIDOC_EXCLUDES += ../assets ../cypress ../swh/web/static ../swh/*/settings
Origin
------
.. autosimple:: swh.web.api.views.origin.api_origins
.. autosimple:: swh.web.api.views.origin.api_origin
.. autosimple:: swh.web.api.views.origin.api_origin_search
......@@ -9,16 +11,10 @@ Origin
.. autosimple:: swh.web.api.views.origin.api_origin_visit
.. autosimple:: swh.web.api.views.origin.api_origin_visit
.. autosimple:: swh.web.save_code_now.api_views.api_save_origin
.. autosimple:: swh.web.save_origin_webhooks.bitbucket.api_origin_save_webhook_bitbucket
.. autosimple:: swh.web.save_origin_webhooks.gitea.api_origin_save_webhook_gitea
.. autosimple:: swh.web.api.views.origin.api_origin_visit_latest
.. autosimple:: swh.web.save_origin_webhooks.github.api_origin_save_webhook_github
.. autosimple:: swh.web.api.views.origin.api_origin_metadata_search
.. autosimple:: swh.web.save_origin_webhooks.gitlab.api_origin_save_webhook_gitlab
.. autosimple:: swh.web.api.views.origin.api_origin_intrinsic_metadata
.. autosimple:: swh.web.save_origin_webhooks.sourceforge.api_origin_save_webhook_sourceforge
.. autosimple:: swh.web.api.views.origin.api_origin_extrinsic_metadata
Provenance
----------
.. autosimple:: swh.web.provenance.api_views.api_provenance_whereis
.. autosimple:: swh.web.provenance.api_views.api_provenance_whereare
Request archival
----------------
.. autosimple:: swh.web.add_forge_now.api_views.api_add_forge_request_create
.. autosimple:: swh.web.add_forge_now.api_views.api_add_forge_request_update
.. autosimple:: swh.web.add_forge_now.api_views.api_add_forge_request_list
.. autosimple:: swh.web.add_forge_now.api_views.api_add_forge_request_get
.. autosimple:: swh.web.add_forge_now.api_views.api_add_forge_request_create
.. autosimple:: swh.web.save_bulk.api_views.api_origin_save_bulk
.. autosimple:: swh.web.save_bulk.api_views.api_origin_save_bulk_requests
.. autosimple:: swh.web.save_bulk.api_views.api_origin_save_bulk_request_info
.. autosimple:: swh.web.save_code_now.api_views.api_save_origin
.. autosimple:: swh.web.save_origin_webhooks.bitbucket.api_origin_save_webhook_bitbucket
.. autosimple:: swh.web.save_origin_webhooks.gitea.api_origin_save_webhook_gitea
.. autosimple:: swh.web.save_origin_webhooks.github.api_origin_save_webhook_github
.. autosimple:: swh.web.save_origin_webhooks.gitlab.api_origin_save_webhook_gitlab
.. autosimple:: swh.web.save_origin_webhooks.sourceforge.api_origin_save_webhook_sourceforge
\ No newline at end of file