diff --git a/cypress/integration/home.spec.js b/cypress/integration/home.spec.js index d82f6084..3fda38ea 100644 --- a/cypress/integration/home.spec.js +++ b/cypress/integration/home.spec.js @@ -1,59 +1,75 @@ /** * Copyright (C) 2019 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.$; const url = '/'; describe('Home Page Tests', function() { it('should display positive stats for each category', function() { cy.server(); cy.route({ method: 'GET', url: this.Urls.stat_counters() }).as('getStatCounters'); cy.visit(url) .wait('@getStatCounters') .wait(500) .get('.swh-counter') .then((counters) => { for (let counter of counters) { let innerText = $(counter).text(); const value = parseInt(innerText.replace(/,/g, '')); assert.isAbove(value, 0); } }); }); it('should display null counters when storage is empty', function() { cy.server(); cy.route({ method: 'GET', url: this.Urls.stat_counters(), response: { 'stat_counters': {}, 'stat_counters_history': {} } }).as('getStatCounters'); cy.visit(url) .wait('@getStatCounters') .wait(500) .get('.swh-counter') .then((counters) => { for (let counter of counters) { const value = parseInt($(counter).text()); assert.equal(value, 0); } }); }); + + it('should redirect to search page when submitting search form', function() { + const searchText = 'git'; + cy.get('#origins-url-patterns') + .type(searchText) + .get('.swh-search-icon') + .click(); + + cy.location('pathname') + .should('equal', this.Urls.browse_search()); + + cy.location('search') + .should('equal', `?q=${searchText}&with_visit=true&with_content=true`); + + }); + }); diff --git a/swh/web/assets/src/bundles/browse/origin-search.js b/swh/web/assets/src/bundles/browse/origin-search.js index ebd66b7f..cb237491 100644 --- a/swh/web/assets/src/bundles/browse/origin-search.js +++ b/swh/web/assets/src/bundles/browse/origin-search.js @@ -1,229 +1,229 @@ /** * Copyright (C) 2018-2020 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} from 'utils/functions'; 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'); }); } function clearOriginSearchResultsTable() { $('#origin-search-results tbody tr').remove(); } 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, origin] of origins.entries()) { let browseUrl = `${Urls.browse_origin()}?origin_url=${origin.url}`; let tableRow = ``; tableRow += `${encodeURI(origin.url)}`; tableRow += ``; tableRow += ``; tableRow += ''; table.append(tableRow); // get async latest visit snapshot and update visit status icon let latestSnapshotUrl = Urls.api_1_origin_visit_latest(origin.url); latestSnapshotUrl += '?require_snapshot=true'; fetch(latestSnapshotUrl) .then(response => response.json()) .then(data => { $(`#visit-type-origin-${i}`).text(data.type); $(`#visit-status-origin-${i}`).children().remove(); if (data) { $(`#visit-status-origin-${i}`).append(''); } else { $(`#visit-status-origin-${i}`).append(''); if ($('#swh-filter-empty-visits').prop('checked')) { $(`#origin-${i}`).remove(); } } }); } fixTableRowsStyle(); } else { $('#swh-origin-search-results').hide(); $('#swh-no-result').text('No origins matching the search criteria were found.'); $('#swh-no-result').show(); } if (linkNext === null) { $('#origins-next-results-button').addClass('disabled'); } else { $('#origins-next-results-button').removeClass('disabled'); } if (linksPrev.length === 0) { $('#origins-prev-results-button').addClass('disabled'); } else { $('#origins-prev-results-button').removeClass('disabled'); } inSearch = false; setTimeout(() => { window.scrollTo(0, 0); }); } function searchOriginsFirst(searchQueryText, limit) { let baseSearchUrl; let searchMetadata = $('#swh-search-origin-metadata').prop('checked'); if (searchMetadata) { baseSearchUrl = new URL(Urls.api_1_origin_metadata_search(), window.location); baseSearchUrl.searchParams.append('fulltext', searchQueryText); } else { baseSearchUrl = new URL(Urls.api_1_origin_search(searchQueryText), window.location); } let withVisit = $('#swh-search-origins-with-visit').prop('checked'); baseSearchUrl.searchParams.append('limit', limit); baseSearchUrl.searchParams.append('with_visit', withVisit); let searchUrl = baseSearchUrl.toString(); searchOrigins(searchUrl); } function searchOrigins(searchUrl) { clearOriginSearchResultsTable(); $('.swh-loading').addClass('show'); let response = fetch(searchUrl) .then(handleFetchError) .then(resp => { response = resp; return response.json(); }) .then(data => { // Save link to the current results page linkCurrent = searchUrl; // Save link to the next results page. linkNext = null; if (response.headers.has('Link')) { let parsedLink = parseLinkHeader(response.headers.get('Link')); if (parsedLink !== undefined) { linkNext = parsedLink; } } // prevLinks is updated by the caller, which is the one to know if // we're going forward or backward in the pages. $('.swh-loading').removeClass('show'); populateOriginSearchResultsTable(data); }) .catch(response => { $('.swh-loading').removeClass('show'); inSearch = false; $('#swh-origin-search-results').hide(); $('#swh-no-result').text(`Error ${response.status}: ${response.statusText}`); $('#swh-no-result').show(); }); } function doSearch() { $('#swh-no-result').hide(); let searchQueryText = $('#origins-url-patterns').val(); inSearch = true; if (searchQueryText.startsWith('swh:')) { // searchQueryText may be a PID so sending search queries to PID resolve endpoint let resolvePidUrl = Urls.api_1_resolve_swh_pid(searchQueryText); fetch(resolvePidUrl) .then(handleFetchError) .then(response => response.json()) .then(data => { // pid has been successfully resolved, // so redirect to browse page window.location = data.browse_url; }) .catch(response => { // display a useful error message if the input // looks like a swh pid response.json().then(data => { $('#swh-origin-search-results').hide(); $('.swh-search-pagination').hide(); $('#swh-no-result').text(data.reason); $('#swh-no-result').show(); }); }); } else { // otherwise, proceed with origins search $('#swh-origin-search-results').show(); $('.swh-search-pagination').show(); searchOriginsFirst(searchQueryText, limit); } } export function initOriginSearch() { $(document).ready(() => { $('#swh-search-origins').submit(event => { event.preventDefault(); let searchQueryText = $('#origins-url-patterns').val().trim(); let withVisit = $('#swh-search-origins-with-visit').prop('checked'); let withContent = $('#swh-filter-empty-visits').prop('checked'); let searchMetadata = $('#swh-search-origin-metadata').prop('checked'); let queryParameters = new URLSearchParams(); queryParameters.append('q', searchQueryText); if (withVisit) { queryParameters.append('with_visit', withVisit); } if (withContent) { queryParameters.append('with_content', withContent); } if (searchMetadata) { queryParameters.append('search_metadata', searchMetadata); } // Update the url, triggering page reload and effective search - window.location.search = `?${queryParameters.toString()}`; + window.location = `${Urls.browse_search()}?${queryParameters.toString()}`; }); $('#origins-next-results-button').click(event => { if ($('#origins-next-results-button').hasClass('disabled') || inSearch) { return; } inSearch = true; linksPrev.push(linkCurrent); searchOrigins(linkNext); event.preventDefault(); }); $('#origins-prev-results-button').click(event => { if ($('#origins-prev-results-button').hasClass('disabled') || inSearch) { return; } inSearch = true; searchOrigins(linksPrev.pop()); event.preventDefault(); }); let urlParams = new URLSearchParams(window.location.search); let query = urlParams.get('q'); let withVisit = urlParams.has('with_visit'); let withContent = urlParams.has('with_content'); let searchMetadata = urlParams.has('search_metadata'); if (query) { $('#origins-url-patterns').val(query); $('#swh-search-origins-with-visit').prop('checked', withVisit); $('#swh-filter-empty-visits').prop('checked', withContent); $('#swh-search-origin-metadata').prop('checked', searchMetadata); doSearch(); } }); } diff --git a/swh/web/templates/browse/search.html b/swh/web/templates/browse/search.html index 5ea1e6fb..23feb540 100644 --- a/swh/web/templates/browse/search.html +++ b/swh/web/templates/browse/search.html @@ -1,81 +1,52 @@ {% extends "./layout.html" %} {% comment %} Copyright (C) 2017-2019 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 {% endcomment %} {% load static %} {% block navbar-content %}

Search archived software

{% endblock %} {% block browse-content %} -
-
- -
- -
-
-
- - -
-
- - -
-
- - -
-
+{% include "includes/origin-search-form.html" %}

Searching origins ...

{% endblock %} diff --git a/swh/web/templates/homepage.html b/swh/web/templates/homepage.html index bec1b6c3..e0afbe2d 100644 --- a/swh/web/templates/homepage.html +++ b/swh/web/templates/homepage.html @@ -1,118 +1,127 @@ {% extends "layout.html" %} {% comment %} Copyright (C) 2017-2019 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 {% endcomment %} {% load static %} +{% load render_bundle from webpack_loader %} + +{% block header %} +{% render_bundle 'browse' %} +{% endblock %} {% block title %}Welcome to the Software Heritage archive{% endblock %} {% block navbar-content %}

Welcome to the Software Heritage archive

{% endblock %} {% block content %} +

Search

+ +{% include "includes/origin-search-form.html" %} +

Overview

The long term goal of the Software Heritage initiative is to collect all publicly available software in source code form together with its development history, replicate it massively to ensure its preservation, and share it with everyone who needs it. The Software Heritage archive is growing over time as we crawl new source code from software projects and development forges.

Content

A significant amount of source code has already been ingested in the Software Heritage archive. It currently includes:

Size

As of today the archive already contains and keeps safe for you the following amount of objects:

Source files
0
Commits
0
Projects
0
Directories
0
Authors
0
Releases
0

Note: the counters and graphs above are based on heuristics that might not reflect the exact size of the archive. While the long-term trends shown and ballpark figures are reliable, individual point-in-time values might not be.

{% endblock %} diff --git a/swh/web/templates/browse/search.html b/swh/web/templates/includes/origin-search-form.html similarity index 59% copy from swh/web/templates/browse/search.html copy to swh/web/templates/includes/origin-search-form.html index 5ea1e6fb..12a9dcb1 100644 --- a/swh/web/templates/browse/search.html +++ b/swh/web/templates/includes/origin-search-form.html @@ -1,81 +1,40 @@ -{% extends "./layout.html" %} - {% comment %} -Copyright (C) 2017-2019 The Software Heritage developers +Copyright (C) 2020 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 {% endcomment %} -{% load static %} - -{% block navbar-content %} -

Search archived software

-{% endblock %} - -{% block browse-content %} -
-
- -
- -

Searching origins ...

-
- - - -{% endblock %}