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 @@ -89,6 +89,209 @@ }); }); + 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() + .get('#swh-filter-empty-visits') + .uncheck() + .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() { + // Setup search + cy.get('#swh-search-origins-with-visit') + .uncheck() + .get('#swh-filter-empty-visits') + .uncheck() + .then(() => { + const searchText = 'many.origins'; + + // Get first page of results + doSearch(searchText); + + 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.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.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() { + // Setup search + cy.get('#swh-search-origins-with-visit') + .uncheck() + .get('#swh-filter-empty-visits') + .uncheck() + .then(() => { + const searchText = 'many.origins'; + + // Get first page of results + doSearch(searchText); + + 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.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.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() { + // Setup search + cy.get('#swh-search-origins-with-visit') + .uncheck() + .get('#swh-filter-empty-visits') + .uncheck() + .then(() => { + const searchText = 'many.origins'; + + // Get first page of results + doSearch(searchText); + + 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.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.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.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 persistent ids', function() { it('should resolve directory', function() { const redirectUrl = this.Urls.browse_directory(origin.content[0].directory); diff --git a/swh/web/assets/src/bundles/browse/origin-search.js b/swh/web/assets/src/bundles/browse/origin-search.js --- a/swh/web/assets/src/bundles/browse/origin-search.js +++ b/swh/web/assets/src/bundles/browse/origin-search.js @@ -8,13 +8,17 @@ import {heapsPermute} from 'utils/heaps-permute'; import {handleFetchError} from 'utils/functions'; -let originPatterns; -let perPage = 100; -let limit = perPage * 2; -let offset = 0; -let currentData = null; +const limit = 100; +let linksPrev = []; +let linkNext = null; +let linkCurrent = null; let inSearch = false; +function parseLinkHeader(s) { + let re = /<(.+)>; rel="next"/; + return s.match(re)[1]; +} + function fixTableRowsStyle() { setTimeout(() => { $('#origin-search-results tbody tr').removeAttr('style'); @@ -25,15 +29,13 @@ $('#origin-search-results tbody tr').remove(); } -function populateOriginSearchResultsTable(origins, offset) { - let localOffset = offset % limit; +function populateOriginSearchResultsTable(origins) { if (origins.length > 0) { $('#swh-origin-search-results').show(); $('#swh-no-result').hide(); clearOriginSearchResultsTable(); let table = $('#origin-search-results tbody'); - for (let i = localOffset; i < localOffset + perPage && i < origins.length; ++i) { - let origin = origins[i]; + for (let [i, origin] of origins.entries()) { let browseUrl = Urls.browse_origin(origin.url); let tableRow = `