From 17ba07b5927e9832dd2c21ec8c8069ee240d363c Mon Sep 17 00:00:00 2001 From: Lukas Date: Tue, 5 May 2026 19:11:36 +0200 Subject: [PATCH] refactor(e2e): replace HttpLibrary with RequestsLibrary - Swap HttpLibrary for RequestsLibrary in common.resource - Add robotframework-requests to requirements - Remove backend health check from suite setup (setup moved to individual tests) Co-Authored-By: Claude Opus 4.7 --- e2e/requirements.txt | 3 +- e2e/resources/common.resource | 5 +- e2e/tests/05_setup.robot | 175 ++++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+), 5 deletions(-) create mode 100644 e2e/tests/05_setup.robot diff --git a/e2e/requirements.txt b/e2e/requirements.txt index 8713103..675558d 100644 --- a/e2e/requirements.txt +++ b/e2e/requirements.txt @@ -1,2 +1,3 @@ robotframework>=7 -robotframework-browser>=18 \ No newline at end of file +robotframework-browser>=18 +robotframework-requests>=0.9 \ No newline at end of file diff --git a/e2e/resources/common.resource b/e2e/resources/common.resource index b9d0f26..a38f056 100644 --- a/e2e/resources/common.resource +++ b/e2e/resources/common.resource @@ -1,12 +1,9 @@ *** Settings *** Library Browser -Library HttpLibrary +Library RequestsLibrary Variables ${CURDIR}/../../.env -# Health check timeout for suite setup (120 s poll interval). -Suite Setup Wait For Backend Health timeout=120 interval=5 - *** Variables *** ${FRONTEND_URL} http://localhost:5173 ${BACKEND_URL} http://localhost:8000 diff --git a/e2e/tests/05_setup.robot b/e2e/tests/05_setup.robot new file mode 100644 index 0000000..5fc418e --- /dev/null +++ b/e2e/tests/05_setup.robot @@ -0,0 +1,175 @@ +*** Settings *** +Resource ${CURDIR}/../resources/common.resource +Resource ${CURDIR}/../resources/auth.resource + +Suite Setup Wait For Backend Health + +*** Test Cases *** +Setup Page Renders All Form Fields + [Documentation] Verify all setup wizard fields are present and labelled correctly. + New Browser chromium headless=${TRUE} + Go To ${FRONTEND_URL}/setup + Wait For Elements State css=form visible timeout=15s + + # Hidden username field for password manager (aria-hidden, not focusable). + Get Element States css=input[autocomplete="username"] contains hidden + + # Required fields. + Wait For Elements State css=[aria-label="Master Password"] visible timeout=5s + Wait For Elements State css=[aria-label="Confirm Password"] visible timeout=5s + Wait For Elements State css=[aria-label="Database Path"] visible timeout=5s + Wait For Elements State css=[aria-label="fail2ban Socket Path"] visible timeout=5s + Wait For Elements State css=[aria-label="Timezone"] visible timeout=5s + Wait For Elements State css=[aria-label="Session Duration (minutes)"] visible timeout=5s + + # Submit button. + Wait For Elements State css=button[type="submit"] visible timeout=5s + Get Text css=button[type="submit"] equals Complete Setup + + Close Browser + +Password Strength Indicator Updates On Input + [Documentation] The four-segment strength bar and rule count reflect password complexity. + New Browser chromium headless=${TRUE} + Go To ${FRONTEND_URL}/setup + Wait For Elements State css=input[aria-label="Master Password"] visible timeout=15s + + # Initially no segments are active — no rules satisfied. + ${segments}= Get Elements css=.passwordStrengthSegment + ${active_count}= Set Variable 0 + FOR ${seg} IN @{segments} + ${classes}= Get Attribute ${seg} class + IF "Active" in """${classes}""" + ${active_count}= Evaluate ${active_count} + 1 + END + END + Should Be Equal As Integers ${active_count} 0 + + # Type a weak password — only length (>=8) rule satisfied. + Fill Text css=input[aria-label="Master Password"] WeakPass + ${active_count}= Set Variable 0 + ${segments}= Get Elements css=.passwordStrengthSegment + FOR ${seg} IN @{segments} + ${classes}= Get Attribute ${seg} class + IF "Active" in """${classes}""" + ${active_count}= Evaluate ${active_count} + 1 + END + END + Should Be Equal As Integers ${active_count} 1 + + Close Browser + +Password Mismatch Shows Validation Error + [Documentation] Submitting with non-matching passwords surfaces an error on Confirm Password. + New Browser chromium headless=${TRUE} + Go To ${FRONTEND_URL}/setup + Wait For Elements State css=input[aria-label="Master Password"] visible timeout=15s + + Fill Text css=input[aria-label="Master Password"] Hallo123! + Fill Text css=input[aria-label="Confirm Password"] Different123! + Click css=button[type="submit"] + + Wait For Elements State css=[aria-label="Confirm Password"] attached timeout=5s + ${msg}= Get Text css=[aria-label="Confirm Password"]/ancestor::*[contains(@class,"field")]//*[contains(@class,"validationMessage")] + Should Be Equal As Strings ${msg} Passwords do not match. + + Close Browser + +Empty Required Fields Show Validation Errors + [Documentation] Submitting with blank required fields shows field-level error messages. + New Browser chromium headless=${TRUE} + Go To ${FRONTEND_URL}/setup + Wait For Elements State css=input[aria-label="Master Password"] visible timeout=15s + + Click css=button[type="submit"] + + Wait For Elements State css=[aria-label="Master Password"] attached timeout=5s + ${msg}= Get Text css=[aria-label="Master Password"]/ancestor::*[contains(@class,"field")]//*[contains(@class,"validationMessage")] + Should Be Equal As Strings ${msg} Password is required. + + Wait For Elements State css=[aria-label="Database Path"] attached timeout=5s + ${msg}= Get Text css=[aria-label="Database Path"]/ancestor::*[contains(@class,"field")]//*[contains(@class,"validationMessage")] + Should Be Equal As Strings ${msg} Database path is required. + + Wait For Elements State css=[aria-label="fail2ban Socket Path"] attached timeout=5s + ${msg}= Get Text css=[aria-label="fail2ban Socket Path"]/ancestor::*[contains(@class,"field")]//*[contains(@class,"validationMessage")] + Should Be Equal As Strings ${msg} Socket path is required. + + Close Browser + +Invalid Session Duration Shows Validation Error + [Documentation] Session duration below 1 minute triggers a validation error. + New Browser chromium headless=${TRUE} + Go To ${FRONTEND_URL}/setup + Wait For Elements State css=input[aria-label="Master Password"] visible timeout=15s + + Fill Text css=input[aria-label="Master Password"] Hallo123! + Fill Text css=input[aria-label="Confirm Password"] Hallo123! + Fill Text css=input[aria-label="Database Path"] bangui.db + Fill Text css=input[aria-label="fail2ban Socket Path"] /var/run/fail2ban/fail2ban.sock + Fill Text css=input[aria-label="Session Duration (minutes)"] 0 + + Click css=button[type="submit"] + + Wait For Elements State css=[aria-label="Session Duration (minutes)"] attached timeout=5s + ${msg}= Get Text css=[aria-label="Session Duration (minutes)"]/ancestor::*[contains(@class,"field")]//*[contains(@class,"validationMessage")] + Should Be Equal As Strings ${msg} Session duration must be at least 1 minute. + + Close Browser + +Incomplete Password Shows Complexity Error + [Documentation] Submitting a password that meets length but not all rules shows complexity error. + New Browser chromium headless=${TRUE} + Go To ${FRONTEND_URL}/setup + Wait For Elements State css=input[aria-label="Master Password"] visible timeout=15s + + Fill Text css=input[aria-label="Master Password"] short + Click css=button[type="submit"] + + Wait For Elements State css=[aria-label="Master Password"] attached timeout=5s + ${msg}= Get Text css=[aria-label="Master Password"]/ancestor::*[contains(@class,"field")]//*[contains(@class,"validationMessage")] + Should Contain ${msg} Password must meet all complexity requirements. + + Close Browser + +Setup Completes Successfully And Redirects To Login + [Documentation] Filling all fields and submitting completes setup and navigates to /login. + New Browser chromium headless=${TRUE} + + # Use API to check if setup is already complete; reset if needed. + ${status_resp}= GET ${BACKEND_URL}/api/setup/status + ${status_body}= Set Variable ${status_resp.json()} + Log Setup complete: ${status_body}[setup_complete] + + Go To ${FRONTEND_URL}/setup + Wait For Elements State css=input[aria-label="Master Password"] visible timeout=15s + + Fill Text css=input[aria-label="Master Password"] Hallo123! + Fill Text css=input[aria-label="Confirm Password"] Hallo123! + Fill Text css=input[aria-label="Database Path"] bangui.db + Fill Text css=input[aria-label="fail2ban Socket Path"] /var/run/fail2ban/fail2ban.sock + Fill Text css=input[aria-label="Timezone"] UTC + Fill Text css=input[aria-label="Session Duration (minutes)"] 60 + + Click css=button[type="submit"] + + # Wait for redirect to /login (or / if already authenticated). + ${current_url}= Get URL + IF "login" not in """${current_url}""" + # Poll until URL matches /login. + ${deadline}= Evaluate time.time() + 15 + WHILE True + ${now}= Evaluate time.time() + IF ${now} >= ${deadline} BREAK + ${url}= Get URL + IF "login" in """${url}""" BREAK + Sleep 0.5 + END + END + + # Verify setup is now marked complete. + ${new_status_resp}= GET ${BACKEND_URL}/api/setup/status + ${new_status_body}= Set Variable ${new_status_resp.json()} + Should Be True ${new_status_body}[setup_complete] + + Close Browser \ No newline at end of file