diff --git a/swh/web/browse/views/content.py b/swh/web/browse/views/content.py index fa646f904f6dda751cb7434a31bbb266308200c5..d60652b7fc39b90ea057b0d08c9b3edd7c028208 100644 --- a/swh/web/browse/views/content.py +++ b/swh/web/browse/views/content.py @@ -180,5 +180,6 @@ def content_display(request, query_string): 'top_right_link_text': mark_safe( '<i class="fa fa-file-text fa-fw" aria-hidden="true">' '</i>Raw File'), - 'origin_context': None + 'origin_context': None, + 'vault_cooking': None }) diff --git a/swh/web/browse/views/directory.py b/swh/web/browse/views/directory.py index d3b9b38284b7d9abc18aa9922dc285479ae5be7d..f1162a7670d69723ce1c89c8b9b95dc620afdcb1 100644 --- a/swh/web/browse/views/directory.py +++ b/swh/web/browse/views/directory.py @@ -88,6 +88,13 @@ def directory_browse(request, sha1_git, path=None): 'number of subdirectories': len(dirs), 'sum of regular file sizes': sum_file_sizes} + vault_cooking = { + 'directory_context': True, + 'directory_id': sha1_git, + 'revision_context': False, + 'revision_id': None + } + return render(request, 'directory.html', {'empty_browse': False, 'heading': 'Directory information', @@ -103,4 +110,5 @@ def directory_browse(request, sha1_git, path=None): 'top_right_link_text': None, 'readme_name': readme_name, 'readme_url': readme_url, - 'origin_context': None}) + 'origin_context': None, + 'vault_cooking': vault_cooking}) diff --git a/swh/web/browse/views/origin.py b/swh/web/browse/views/origin.py index 76008fe753367d8cf22b89a377f9cb14c84ab26e..7ed233ccc5383fa8f2496cb2476f2d1d4c14f8de 100644 --- a/swh/web/browse/views/origin.py +++ b/swh/web/browse/views/origin.py @@ -309,6 +309,13 @@ def origin_directory_browse(request, origin_type, origin_url, 'revision id': revision_id, 'browse revision url': browse_rev_url} + vault_cooking = { + 'directory_context': True, + 'directory_id': sha1_git, + 'revision_context': True, + 'revision_id': revision_id + } + return render(request, 'directory.html', {'empty_browse': False, 'heading': 'Directory information', @@ -327,7 +334,8 @@ def origin_directory_browse(request, origin_type, origin_url, ), 'readme_name': readme_name, 'readme_url': readme_url, - 'origin_context': origin_context}) + 'origin_context': origin_context, + 'vault_cooking': vault_cooking}) @browse_route(r'origin/(?P<origin_type>[a-z]+)/url/(?P<origin_url>.+)/visit/(?P<timestamp>.+)/content/(?P<path>.+)/', # noqa @@ -461,7 +469,9 @@ def origin_content_display(request, origin_type, origin_url, path, 'top_right_link_text': mark_safe( '<i class="fa fa-file-text fa-fw" aria-hidden="true">' '</i>Raw File'), - 'origin_context': origin_context}) + 'origin_context': origin_context, + 'vault_cooking': None + }) def _gen_directory_link(url_args, query_params, link_text): @@ -595,7 +605,8 @@ def origin_log_browse(request, origin_type, origin_url, timestamp=None): 'top_right_link': None, 'top_right_link_text': None, 'include_top_navigation': True, - 'origin_context': origin_context}) + 'origin_context': origin_context, + 'vault_cooking': None}) @browse_route(r'origin/(?P<origin_type>[a-z]+)/url/(?P<origin_url>.+)/visit/(?P<timestamp>.+)/branches/', # noqa @@ -747,7 +758,8 @@ def origin_releases_browse(request, origin_type, origin_url, timestamp=None): 'displayed_releases': displayed_releases, 'prev_releases_url': prev_releases_url, 'next_releases_url': next_releases_url, - 'origin_context': origin_context}) + 'origin_context': origin_context, + 'vault_cooking': None}) @browse_route(r'origin/(?P<origin_type>[a-z]+)/url/(?P<origin_url>.+)/', @@ -826,7 +838,8 @@ def origin_browse(request, origin_type=None, origin_url=None): 'visits_splitted': visits_splitted, 'origin_info': origin_info, 'browse_url_base': '/browse/origin/%s/url/%s/' % - (origin_type, origin_url)}) + (origin_type, origin_url), + 'vault_cooking': None}) @browse_route(r'origin/search/(?P<url_pattern>.+)/', diff --git a/swh/web/browse/views/person.py b/swh/web/browse/views/person.py index 1ab88143b28c74160485879bcfadee165231ae63..49959b974fcb7b0d55428469e3ba764b5db8c54b 100644 --- a/swh/web/browse/views/person.py +++ b/swh/web/browse/views/person.py @@ -38,4 +38,5 @@ def person_browse(request, person_id): 'top_panel_collapsible': False, 'top_panel_text': 'SWH object: Person', 'swh_object_metadata': person, - 'main_panel_visible': False}) + 'main_panel_visible': False, + 'vault_cooking': None}) diff --git a/swh/web/browse/views/revision.py b/swh/web/browse/views/revision.py index c9462ea198a2644fb3ba7e169e38742f287c1ee5..60c6db31895bcf6a48bcc87dc306670ad9aa91e8 100644 --- a/swh/web/browse/views/revision.py +++ b/swh/web/browse/views/revision.py @@ -165,6 +165,12 @@ def revision_browse(request, sha1_git): query_params=query_params) history_url = get_revision_log_url(sha1_git, origin_context) + vault_cooking = { + 'directory_context': True, + 'directory_id': dir_id, + 'revision_context': True, + 'revision_id': sha1_git + } return render(request, 'revision.html', {'empty_browse': False, @@ -188,7 +194,8 @@ def revision_browse(request, sha1_git): 'top_right_link_text': mark_safe( '<i class="fa fa-history fa-fw" aria-hidden="true"></i>' 'History' - )}) + ), + 'vault_cooking': vault_cooking}) NB_LOG_ENTRIES = 20 @@ -263,4 +270,5 @@ def revision_log_browse(request, sha1_git): 'top_right_link': None, 'top_right_link_text': None, 'include_top_navigation': False, - 'origin_context': None}) + 'origin_context': None, + 'vault_cooking': None}) diff --git a/swh/web/static/css/style.css b/swh/web/static/css/style.css index 1e5746abb4a7903283076db6cb7b83b53c2971ae..40d7c31c1a51c7a7ef4f69c27e6776cf04810788 100644 --- a/swh/web/static/css/style.css +++ b/swh/web/static/css/style.css @@ -457,6 +457,11 @@ fieldset[disabled] .btn-swh.active { .swh-table { border-bottom: none !important; + margin-bottom: 0px !important; +} + +.swh-table td { + vertical-align: middle !important; } .swh-counter { @@ -508,6 +513,15 @@ fieldset[disabled] .btn-swh.active { border: none; } +.popover { + max-width: 100%; +} + +.btn-swh-vault { + border-top-right-radius: 4px !important; + border-bottom-right-radius: 4px !important; +} + .pager a { outline: none; } @@ -608,4 +622,23 @@ fieldset[disabled] .btn-swh.active { transform: rotate(45deg); -webkit-transform: rotate(45deg); z-index: 2000; - } \ No newline at end of file + } + + .modal { + text-align: center; + padding: 0!important; + } + + .modal:before { + content: ''; + display: inline-block; + height: 100%; + vertical-align: middle; + margin-right: -4px; + } + + .modal-dialog { + display: inline-block; + text-align: left; + vertical-align: middle; + } diff --git a/swh/web/templates/browse.html b/swh/web/templates/browse.html index 418db6613901d588655282cd3c88f92c8d3b58c4..16ab8f396fcb5d7d9f1958bbb9d55961f03012ef 100644 --- a/swh/web/templates/browse.html +++ b/swh/web/templates/browse.html @@ -11,6 +11,9 @@ <li> <a href="#help" data-toggle="tab" style="outline:none;">Help</a> </li> + <li> + <a href="#vault" data-toggle="tab" style="outline:none;">Vault</a> + </li> <li> <a href="#browse" data-toggle="tab" style="outline:none;">Browse</a> </li> @@ -31,6 +34,10 @@ {% include "includes/browse-help.html" %} </div> + <div class="tab-pane" id="vault"> + {% include "includes/vault-ui.html" %} + </div> + <div class="tab-pane" id="browse"> {% if empty_browse %} @@ -110,26 +117,55 @@ <script> + var browse_tabs_hash = ["#browse", "#search", "#help", "#vault"]; + function removeHash () { history.replaceState("", document.title, window.location.pathname + window.location.search); } - var browse_tabs_hash = ["#browse", "#search", "#help"]; + function show_tab(hash) { + $('.navbar-nav.swh-browse-nav a[href="' + hash + '"]').tab('show'); + } - // Javascript to enable link to tab function show_requested_tab() { var hash = window.location.hash; if (hash && browse_tabs_hash.indexOf(hash) == -1) { return; } if (hash) { - $('.navbar-nav.swh-browse-nav a[href="' + hash + '"]').tab('show'); + show_tab(hash); } else { - $('.navbar-nav.swh-browse-nav a[href="#browse"]').tab('show'); + show_tab('#browse'); } - window.scrollTo(0, 0); } + $('[data-toggle=popover]:not([data-popover-content])').popover(); + $('[data-toggle=popover][data-popover-content]').popover({ + html : true, + container: 'body', + trigger: 'focus', + content: function() { + var content = $(this).attr("data-popover-content"); + return $(content).children(".popover-body").html(); + }, + title: function() { + var title = $(this).attr("data-popover-content"); + return $(title).children(".popover-heading").html(); + } + }).click(function(e) { + e.preventDefault(); + });; + + // Change hash for page reload + $('.nav-tabs a').on('shown.bs.tab', function (e) { + if (e.target.hash != '#browse') { + window.location.hash = e.target.hash; + } else { + $('.navbar-nav.swh-browse-nav a[href="#browse"]').tab('show'); + } + window.scrollTo(0, 0); + }); + // show requested tab when loading the page $(document).ready(function() { diff --git a/swh/web/templates/includes/top-navigation.html b/swh/web/templates/includes/top-navigation.html index 629e65a404b32a1c945a0e7fda9cf53613b8c5ea..0e6377c8e325e5c9b08538d8bee88b2086dfeeb1 100644 --- a/swh/web/templates/includes/top-navigation.html +++ b/swh/web/templates/includes/top-navigation.html @@ -1,3 +1,4 @@ +{% load swh_templatetags %} <div class="swh-browse-top-navigation"> {% if origin_context and origin_context.branch %} @@ -61,9 +62,12 @@ </div> {% endif %} - {% if top_right_link %} - <a href="{{ top_right_link | safe }}" class="btn btn-md btn-swh pull-right" role="button">{{ top_right_link_text }}</a> - {% endif %} + <div class="btn-group pull-right"> + {% if top_right_link %} + <a href="{{ top_right_link | safe }}" class="btn btn-md btn-swh" role="button">{{ top_right_link_text }}</a> + {% endif %} + {% include "includes/vault-create-tasks.html" %} + </div> {% include "includes/breadcrumbs.html" %} diff --git a/swh/web/templates/includes/vault-create-tasks.html b/swh/web/templates/includes/vault-create-tasks.html new file mode 100644 index 0000000000000000000000000000000000000000..c04703cb2f78005beb2adaafd87b3ea071960e92 --- /dev/null +++ b/swh/web/templates/includes/vault-create-tasks.html @@ -0,0 +1,157 @@ +{% if vault_cooking %} + <a class="btn btn-md btn-swh btn-swh-vault" data-placement="bottom" data-popover-content="#vault-popover" data-toggle="popover" href="#" tabindex="0"> + <i class="fa fa-download fa-fw" aria-hidden="true"></i>Download + </a> + <div class="hidden" id="vault-popover"> + <div class="popover-heading"> + Request download from the Software Heritage Vault + </div> + <div class="popover-body"> + <div class="btn-group-vertical"> + {% if vault_cooking.directory_context %} + <button id="vault-cook-directory" type="button" class="btn btn-md btn-swh" data-toggle="modal" data-target="#vault-cook-directory-modal">Cook a standard archive from the current directory</button> + {% endif %} + {% if vault_cooking.revision_context %} + <button id="vault-cook-revision" type="button" class="btn btn-md btn-swh" data-toggle="modal" data-target="#vault-cook-revision-modal">Cook a git fast-import archive from the current revision</button> + {% endif %} + </div> + </div> + </div> + <div class="modal fade" id="vault-cook-directory-modal" tabindex="-1" role="dialog" aria-labelledby="vault-cook-directory-modal-label" aria-hidden="true"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> + <h4 class="modal-title" id="vault-cook-directory-modal-label">Cook and download a directory from the Software Heritage Vault</h4> + </div> + <div class="modal-body"> + <p> + You have requested the cooking of the directory with identifier <strong>{{ vault_cooking.directory_id }}</strong> + into a standard tar.gz archive. + </p> + <p> + Are you sure you want to continue ? + </p> + <form> + <div class="form-group"> + <label for="email">(Optional) Send download link once it is available to that email address:</label> + <input type="email" class="form-control" id="swh-vault-directory-email"> + </div> + </form> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-md btn-swh" data-dismiss="modal">Cancel</button> + <button type="button" class="btn btn-md btn-swh" onclick="vault_cook_directory_archive()">Ok</button> + </div> + </div> + </div> + </div> + <div class="modal fade" id="vault-cook-revision-modal" tabindex="-1" role="dialog" aria-labelledby="vault-cook-revision-modal-label" aria-hidden="true"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> + <h4 class="modal-title" id="vault-cook-revision-modal-label">Cook and download a revision from the Software Heritage Vault</h4> + </div> + <div class="modal-body"> + <p> + You have requested the cooking of the revision with identifier <strong>{{ vault_cooking.revision_id }}</strong> + into a git fast-import archive. + </p> + <p> + Are you sure you want to continue ? + </p> + <form> + <div class="form-group"> + <label for="email">(Optional) Send download link once it is available to that email address:</label> + <input type="email" class="form-control" id="swh-vault-revision-email"> + </div> + </form> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-md btn-swh" data-dismiss="modal">Cancel</button> + <button type="button" class="btn btn-md btn-swh" onclick="vault_cook_revision_archive()">Ok</button> + </div> + </div> + </div> + </div> + <div class="modal fade" id="invalid-email-modal" tabindex="-1" role="dialog" aria-labelledby="invalid-email-modal-label" aria-hidden="true"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> + <h4 class="modal-title" id="invalid-email-modal-label">Invalid Email !</h4> + </div> + <div class="modal-body"> + <p>The provided email is not well-formed.</p> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-md btn-swh" data-dismiss="modal">Ok</button> + </div> + </div> + </div> + </div> + <script> + var cook_dir_url = "{% url 'vault-cook-directory' 'ffff' %}"; + var cook_rev_url = "{% url 'vault-cook-revision_gitfast' 'ffff' %}"; + function add_vault_cooking_task(cooking_task) { + var vault_cooking_tasks = JSON.parse(sessionStorage.getItem("swh-vault-cooking-tasks")); + if (!vault_cooking_tasks) { + vault_cooking_tasks = []; + } + if (vault_cooking_tasks.find(function(val) { + return val.object_type == cooking_task.object_type && + val.object_id == cooking_task.object_id}) == undefined) { + var cooking_url; + if (cooking_task.object_type == 'directory') { + cooking_url = cook_dir_url.replace('ffff', cooking_task.object_id); + } else { + cooking_url = cook_rev_url.replace('ffff', cooking_task.object_id); + } + if (cooking_task.email) { + cooking_url += "?email=" + cooking_task.email; + } + $.ajax({ + url : cooking_url, + type : 'POST', + success: function() { + vault_cooking_tasks.push(cooking_task); + sessionStorage.setItem("swh-vault-cooking-tasks", JSON.stringify(vault_cooking_tasks)); + show_tab('#vault'); + } + }); + } + } + function validateEmail(email) { + var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return re.test(String(email).toLowerCase()); + } + function vault_cook_directory_archive() { + var email = $('#swh-vault-directory-email').val().trim(); + if (!email || validateEmail(email)) { + $('#vault-cook-directory-modal').modal('hide'); + var cooking_task = {'object_type': 'directory', + 'object_id': '{{ vault_cooking.directory_id }}', + 'email': email, + 'status': 'new'}; + add_vault_cooking_task(cooking_task); + + } else { + $('#invalid-email-modal').modal('show'); + } + } + function vault_cook_revision_archive() { + var email = $('#swh-vault-revision-email').val().trim(); + if (!email || validateEmail(email)) { + $('#vault-cook-revision-modal').modal('hide'); + var cooking_task = {'object_type': 'revision', + 'object_id': '{{ vault_cooking.revision_id }}', + 'email': email, + 'status': 'new'}; + add_vault_cooking_task(cooking_task); + } else { + $('#invalid-email-modal').modal('show'); + } + } + </script> +{% endif %} \ No newline at end of file diff --git a/swh/web/templates/includes/vault-ui.html b/swh/web/templates/includes/vault-ui.html new file mode 100644 index 0000000000000000000000000000000000000000..c0dc1d03d020ed0a113e1541901595a96062439b --- /dev/null +++ b/swh/web/templates/includes/vault-ui.html @@ -0,0 +1,142 @@ +{% load static %} + +<div class="panel panel-default" style="overflow-x: auto;"> + <div class="panel-heading"> + <h2>Download content from the Software Heritage Vault</h2> + </div> + <div class="panel-body"> + <p> + This interface enables to track the status of the different Software Heritage + Vault cooking tasks created during the current browsing session. + </p> + <p> + Once a cooking task is finished, a link will be made available in order to + download the associated archive. + </p> + <div class="table-responsive"> + <table class="table swh-table" id="vault-cooking-tasks"> + <thead> + <tr> + <th>Object type</th> + <th>Object id</th> + <th>Email notification</th> + <th>Cooking status</th> + <th style="width: 320px"></th> + </tr> + </thead> + <tbody></tbody> + </table> + </div> + </div> +</div> + +<script> + var cook_dir_url = "{% url 'vault-cook-directory' 'ffff' %}"; + var cook_rev_url = "{% url 'vault-cook-revision_gitfast' 'ffff' %}"; + var browse_dir_url = "{% url 'browse-directory' 'ffff' %}"; + var browse_rev_url = "{% url 'browse-revision' 'ffff' %}"; + + var progress = '<div class="progress" style="margin-bottom: 0px;"> \ + <div class="progress-bar progress-bar-success progress-bar-striped" \ + role="progressbar" aria-valuenow="100" aria-valuemin="0" \ + aria-valuemax="100" style="width: 100%;height: 100%;"> \ + </div> \ + </div>'; + + function check_vault_cooking_tasks() { + var vault_cooking_tasks = JSON.parse(sessionStorage.getItem("swh-vault-cooking-tasks")); + if (!vault_cooking_tasks) { + return; + } + var cooking_urls = []; + var tasks = {}; + for (var i = 0 ; i < vault_cooking_tasks.length ; ++i) { + var cooking_task = vault_cooking_tasks[i]; + tasks[cooking_task.object_id] = cooking_task; + var cooking_url; + if (cooking_task.object_type == 'directory') { + cooking_url = cook_dir_url.replace('ffff', cooking_task.object_id); + } else { + cooking_url = cook_rev_url.replace('ffff', cooking_task.object_id); + } + if (cooking_task.status != 'done' && cooking_task.status != 'failed') { + cooking_urls.push($.ajax(cooking_url)); + } + } + $.when.apply($, cooking_urls).then(function() { + $("#vault-cooking-tasks tbody tr").remove(); + var table = $("#vault-cooking-tasks tbody"); + for (var i = 0 ; i < cooking_urls.length ; ++i) { + var resp; + if (cooking_urls.length == 1) { + resp = arguments[i]; + } else { + resp = arguments[i][0]; + } + var cooking_task = tasks[resp.obj_id]; + cooking_task.status = resp.status; + cooking_task.fetch_url = resp.fetch_url; + } + for (var i = 0 ; i < vault_cooking_tasks.length ; ++i) { + var cooking_task = vault_cooking_tasks[i]; + var browse_url; + if (cooking_task.object_type == 'directory') { + browse_url = browse_dir_url.replace('ffff', cooking_task.object_id); + } else { + browse_url = browse_rev_url.replace('ffff', cooking_task.object_id); + } + + var progress_bar = $.parseHTML(progress)[0]; + var progress_bar_content = $(progress_bar).find('.progress-bar'); + if (cooking_task.status == 'failed') { + progress_bar_content.css('background-image', 'none'); + progress_bar_content.css('background-color', 'red'); + } + progress_bar_content.text(cooking_task.status); + if (cooking_task.status == 'pending') { + progress_bar_content.addClass('active'); + } else if (cooking_task.status == 'done') { + progress_bar_content.removeClass('progress-bar-striped'); + } + var table_row; + if (cooking_task.object_type == 'directory') { + table_row = '<tr title="Once downloaded, the directory can be extracted with the ' + + 'following command:\n\n$ tar xvzf ' + cooking_task.object_id + '.tar.gz">'; + } else { + table_row = '<tr title="Once downloaded, the git repository can be imported with the ' + + 'following commands:\n\n$ git init\n$ zcat ' + cooking_task.object_id + '.gitfast.gz | git fast-import">'; + } + if (cooking_task.object_type == 'directory') { + table_row += '<td><i class="fa fa-folder fa-fw" aria-hidden="true"></i>directory</td>'; + } else { + table_row += '<td><i class="octicon octicon-git-commit fa-fw"></i>revision</td>'; + } + table_row += '<td><a href="' + browse_url + '">' + cooking_task.object_id + '</a></td>'; + table_row += '<td>' + (cooking_task.email || 'none') + '</td>'; + table_row += '<td>' + progress_bar.outerHTML + '</td>'; + var dl_link = 'Waiting for download link to be available'; + if (cooking_task.status == 'done') { + dl_link = '<a class="btn btn-md btn-swh" href="' + cooking_task.fetch_url + + '"><i class="fa fa-download fa-fw" aria-hidden="true"></i>Download</a>'; + } + table_row += '<td style="width: 320px">' + dl_link + '</td>'; + table_row += '</tr>'; + table.append(table_row); + } + sessionStorage.setItem("swh-vault-cooking-tasks", JSON.stringify(vault_cooking_tasks)); + check_vault_id = setTimeout(check_vault_cooking_tasks, polling_interval); + }); + } + + var polling_interval = 5000; + + var check_vault_id = setTimeout(check_vault_cooking_tasks, polling_interval); + + $(document).on('shown.bs.tab', 'a[data-toggle="tab"]', function (e) { + if (e.target.text == 'Vault') { + clearTimeout(check_vault_id); + check_vault_cooking_tasks(); + } + }); + +</script> \ No newline at end of file diff --git a/swh/web/tests/browse/views/test_directory.py b/swh/web/tests/browse/views/test_directory.py index 42fe069d2ad37cdbd3e5d983dd1eb70866764d70..d9181b58c533482ba5ede7bbb2db16b0b03b4f15 100644 --- a/swh/web/tests/browse/views/test_directory.py +++ b/swh/web/tests/browse/views/test_directory.py @@ -81,6 +81,8 @@ class SwhBrowseDirectoryTest(SWHWebTestBase, TestCase): self.assertContains(resp, '<a href="%s">%s</a>' % (dir_url, p['name'])) + self.assertContains(resp, '<button id="vault-cook-directory"') + @patch('swh.web.browse.utils.service') @istest def root_directory_view(self, mock_service): diff --git a/swh/web/tests/browse/views/test_origin.py b/swh/web/tests/browse/views/test_origin.py index fc68019a7e103aae32565fa797a03e9a74f76b0e..09678da808be05951ba406d2c032a58ee7eeef96 100644 --- a/swh/web/tests/browse/views/test_origin.py +++ b/swh/web/tests/browse/views/test_origin.py @@ -387,6 +387,8 @@ class SwhBrowseOriginTest(SWHWebTestBase, TestCase): query_params=query_params) self.assertContains(resp, '<a href="%s">' % root_dir_release_url) + self.assertContains(resp, '<button id="vault-cook-directory"') + self.assertContains(resp, '<button id="vault-cook-revision"') @patch('swh.web.browse.utils.get_origin_visits') @patch('swh.web.browse.utils.get_origin_visit_occurrences') diff --git a/swh/web/tests/browse/views/test_revision.py b/swh/web/tests/browse/views/test_revision.py index d6bb595a4d8f3c49f252d8541272c2715596f4b7..024942f5850dc29cdb6e762875c4e31003c8333e 100644 --- a/swh/web/tests/browse/views/test_revision.py +++ b/swh/web/tests/browse/views/test_revision.py @@ -116,6 +116,9 @@ class SwhBrowseRevisionTest(SWHWebTestBase, TestCase): self.assertContains(resp, '<a href="%s">%s</a>' % (parent_url, parent)) + self.assertContains(resp, '<button id="vault-cook-directory"') + self.assertContains(resp, '<button id="vault-cook-revision"') + @patch('swh.web.browse.views.revision.service') @istest def revision_log_browse(self, mock_service):