diff --git a/tests/e2e/ui/test_service_store.py b/tests/e2e/ui/test_service_store.py new file mode 100644 index 0000000..a5bafb4 --- /dev/null +++ b/tests/e2e/ui/test_service_store.py @@ -0,0 +1,175 @@ +""" +Service Store E2E tests. + +Tests that the admin can install and remove store services via the /store page. +Requires a running PIC stack with access to the service store index and registry. + +Run with: + pytest tests/e2e/ui/test_service_store.py -v --base-url http:// +""" +import pytest + +pytestmark = pytest.mark.ui + +STORE_ROUTE = '/services' + +# Services to install in dependency order (webmail requires email) +INSTALL_ORDER = ['calendar', 'files', 'email', 'webmail'] + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def _goto_store(page, webui_base): + page.goto(f"{webui_base}{STORE_ROUTE}") + page.wait_for_load_state('networkidle') + + +def _service_card(page, service_name): + """Return the card element containing the named service.""" + return page.locator('.card', has=page.get_by_text(service_name, exact=False)).first + + +def _is_installed(page, service_name): + card = _service_card(page, service_name) + return card.get_by_text('Installed', exact=False).is_visible() + + +def _install_service(page, webui_base, service_name, timeout_ms=180_000): + """Click Install on a service card and wait until the card shows Installed.""" + _goto_store(page, webui_base) + card = _service_card(page, service_name) + install_btn = card.get_by_role('button', name='Install') + install_btn.click() + # Wait for the Install button to disappear (replaced by Remove) or for + # the Installed badge to appear — whichever comes first. + card.get_by_text('Installed', exact=False).wait_for(state='visible', timeout=timeout_ms) + + +def _remove_service(page, webui_base, service_name, timeout_ms=60_000): + """Click Uninstall on a service card and confirm, then wait until Install reappears.""" + _goto_store(page, webui_base) + card = _service_card(page, service_name) + card.get_by_role('button', name='Uninstall').click() + # A confirmation dialog appears — click the confirm Uninstall button + page.get_by_role('button', name='Uninstall Service').wait_for(state='visible', timeout=5000) + page.get_by_role('button', name='Uninstall Service').click() + card.get_by_role('button', name='Install').wait_for(state='visible', timeout=timeout_ms) + + +# --------------------------------------------------------------------------- +# Tests +# --------------------------------------------------------------------------- + +def test_store_page_loads(admin_page, webui_base): + """Store page must load and list available services without errors.""" + page = admin_page + _goto_store(page, webui_base) + + # Should not show a generic error message + assert 'Could not load the service store' not in page.content(), ( + 'Store page showed error: could not load the service store' + ) + + # At least one service card should be visible + cards = page.locator('.card').all() + assert len(cards) > 0, 'No service cards found on the store page' + + +def test_store_shows_known_services(admin_page, webui_base): + """Store page must list email, calendar, files, and webmail.""" + page = admin_page + _goto_store(page, webui_base) + + for name in ('Email Server', 'Calendar', 'File Storage', 'Webmail'): + assert page.get_by_text(name, exact=False).first.is_visible(), ( + f"Expected service '{name}' not visible on store page" + ) + + +def test_install_calendar(admin_page, webui_base): + """Admin can install the calendar service.""" + page = admin_page + _goto_store(page, webui_base) + + if _is_installed(page, 'Calendar'): + pytest.skip('calendar already installed — skipping install test') + + _install_service(page, webui_base, 'Calendar & Contacts', timeout_ms=180_000) + assert _is_installed(page, 'Calendar'), ( + 'Calendar service card did not show Installed after install' + ) + + +def test_install_files(admin_page, webui_base): + """Admin can install the file storage service.""" + page = admin_page + _goto_store(page, webui_base) + + if _is_installed(page, 'File Storage'): + pytest.skip('files already installed — skipping install test') + + _install_service(page, webui_base, 'File Storage', timeout_ms=180_000) + assert _is_installed(page, 'File Storage'), ( + 'Files service card did not show Installed after install' + ) + + +def test_install_email(admin_page, webui_base): + """Admin can install the email service.""" + page = admin_page + _goto_store(page, webui_base) + + if _is_installed(page, 'Email Server'): + pytest.skip('email already installed — skipping install test') + + _install_service(page, webui_base, 'Email Server', timeout_ms=300_000) + assert _is_installed(page, 'Email Server'), ( + 'Email service card did not show Installed after install' + ) + + +def test_install_webmail(admin_page, webui_base): + """Admin can install webmail after email is installed.""" + page = admin_page + _goto_store(page, webui_base) + + if not _is_installed(page, 'Email Server'): + pytest.skip('email not installed — webmail requires email first') + + if _is_installed(page, 'Webmail'): + pytest.skip('webmail already installed — skipping install test') + + _install_service(page, webui_base, 'Webmail', timeout_ms=180_000) + assert _is_installed(page, 'Webmail'), ( + 'Webmail service card did not show Installed after install' + ) + + +def test_installed_services_appear_on_dashboard(admin_page, webui_base): + """After installation, services should appear as links on the dashboard.""" + page = admin_page + _goto_store(page, webui_base) + + page.goto(f"{webui_base}/") + page.wait_for_load_state('networkidle') + + # Check that at least the Cell Home link is present + assert page.get_by_text('Cell Home', exact=False).is_visible(), ( + 'Dashboard does not show the Cell Home service link' + ) + + +def test_uninstall_webmail(admin_page, webui_base): + """Admin can uninstall the webmail service.""" + page = admin_page + _goto_store(page, webui_base) + + if not _is_installed(page, 'Webmail'): + pytest.skip('webmail not installed — skipping uninstall test') + + _remove_service(page, webui_base, 'Webmail') + assert not _is_installed(page, 'Webmail'), ( + 'Webmail service card still shows Installed after uninstall' + )