diff --git a/plinth/tests/test_package.py b/plinth/tests/test_package.py
index e553f644b..137418a97 100644
--- a/plinth/tests/test_package.py
+++ b/plinth/tests/test_package.py
@@ -3,6 +3,7 @@
Test module for package module.
"""
+import time
import unittest
from unittest.mock import Mock, call, patch
@@ -338,23 +339,46 @@ def test_packages_find_conflicts(packages_installed_):
assert component.find_conflicts() == ['package1', 'package2']
+@patch('plinth.package.refresh_package_lists')
@patch('apt.Cache')
@patch('pathlib.Path')
-def test_packages_is_available(path_class, cache):
+def test_packages_is_available(path_class, cache, refresh_package_lists):
"""Test checking for available packages."""
path = Mock()
path_class.return_value = path
- path.iterdir.return_value = [Mock()]
+ # Packages found in cache
component = Packages('test-component', ['package1', 'package2'])
- assert component.is_available()
-
- path.iterdir.return_value = [Mock(), Mock()]
cache.return_value = ['package1', 'package2']
assert component.is_available()
+ path_class.assert_not_called()
+ refresh_package_lists.assert_not_called()
+ # Packages not found, cache exists and is fresh
cache.return_value = ['package1']
+ path.exists.return_value = True
+ path.stat.return_value.st_mtime = time.time()
assert not component.is_available()
+ refresh_package_lists.assert_not_called()
+
+ # Packages not found, cache does not exist
+ cache.return_value = ['package1']
+ path.exists.return_value = False
+ assert not component.is_available()
+ refresh_package_lists.assert_called_once()
+
+ # Packages not found, cache is stale
+ cache.return_value = ['package1']
+ refresh_package_lists.reset_mock()
+ path.exists.return_value = True
+ path.stat.return_value.st_mtime = time.time() - 7200
+ assert not component.is_available()
+ refresh_package_lists.assert_called_once()
+
+ # Packages not found, cache is stale, but packages found after refresh
+ cache.side_effect = [['package1'], ['package1', 'package2']]
+ refresh_package_lists.reset_mock()
+ assert component.is_available()
def test_packages_installed():
diff --git a/static/themes/default/js/main.js b/static/themes/default/js/main.js
index dbb4e8bd6..726a72714 100644
--- a/static/themes/default/js/main.js
+++ b/static/themes/default/js/main.js
@@ -210,7 +210,7 @@ function setSelectAllValue(parent) {
/*
* Check whether an app is available on its setup page.
*/
-document.addEventListener('DOMContentLoaded', function(event) {
+document.addEventListener('DOMContentLoaded', async () => {
const checkingElement = document.querySelector('.app-checking-availability');
if (!checkingElement)
return;
@@ -225,11 +225,7 @@ document.addEventListener('DOMContentLoaded', function(event) {
function setInstallButtonState(enable) {
const installButton = document.querySelector('.install-button');
- if (enable) {
- installButton.removeAttribute('disabled');
- } else {
- installButton.setAttribute('disabled', 'disabled');
- }
+ installButton?.setAttribute('disabled', !enable);
}
function unavailable() {
@@ -241,37 +237,30 @@ document.addEventListener('DOMContentLoaded', function(event) {
const element = document.querySelector('.app-checking-availability-error');
element.classList.remove('d-none');
checkingElement.classList.add('d-none');
- setInstallButtonState(true); // Allow trying installation
+ setInstallButtonState(true); // Allow trying installation
}
- let request = new XMLHttpRequest();
- request.timeout = 2 * 60 * 1000; // 2 minutes
- request.onload = function() {
- // Remove the progress spinner
+ try {
+ setInstallButtonState(false);
+ const response = await fetch(`/plinth/is-available/${appId}/`, {
+ timeout: 2 * 60 * 1000 // 2 minutes
+ });
+
checkingElement.classList.add('d-none');
- let available = false;
- if (this.status === 200) {
- try {
- const response = JSON.parse(this.responseText);
- if (response.is_available === true) {
- setInstallButtonState(true);
- } else if (response.is_available === false) {
- unavailable();
- } else {
- error();
- }
- } catch (e) {
+ if (response.ok) {
+ const data = await response.json();
+ if (data.is_available === true) {
+ setInstallButtonState(true);
+ } else if (data.is_available === false) {
+ unavailable();
+ } else {
error();
}
} else {
error();
}
- };
- request.onerror = error;
- request.ontimeout = error;
-
- request.open('GET', `/plinth/is-available/${appId}/`, true);
- setInstallButtonState(false);
- request.send();
+ } catch {
+ error();
+ }
});