Files
clawd/dashboard/test_habits_form_submit.py

210 lines
8.7 KiB
Python

#!/usr/bin/env python3
"""
Tests for Story 11.0: Frontend - Create habit from form
Acceptance Criteria:
1. Form submit calls POST /api/habits with name and frequency
2. Shows loading state on submit (button disabled)
3. On success: modal closes, list refreshes, new habit appears
4. On error: shows error message in modal, modal stays open
5. Input field cleared after successful creation
6. Tests for form submission pass
"""
import os
import sys
def test_form_submit_api_call():
"""Test that createHabit calls POST /api/habits with correct data"""
with open('dashboard/habits.html', 'r') as f:
html = f.read()
# Check that createHabit function exists
assert 'async function createHabit()' in html, "createHabit function should exist"
# Check that it calls POST /api/habits
assert "fetch('/api/habits'" in html, "Should fetch /api/habits endpoint"
assert "method: 'POST'" in html, "Should use POST method"
# Check that it sends name and frequency in JSON body
assert "body: JSON.stringify({ name, frequency })" in html, "Should send name and frequency in request body"
# Check that frequency is read from checked radio button
assert "document.querySelector('input[name=\"frequency\"]:checked').value" in html, "Should read frequency from radio buttons"
print("✓ Form submission calls POST /api/habits with name and frequency")
def test_loading_state_on_submit():
"""Test that button is disabled during submission"""
with open('dashboard/habits.html', 'r') as f:
html = f.read()
# Check that createBtn is referenced
assert "createBtn = document.getElementById('habitCreateBtn')" in html, "Should get create button element"
# Check that button is disabled before fetch
assert "createBtn.disabled = true" in html, "Button should be disabled during submission"
# Check that button text changes to loading state
assert "createBtn.textContent = 'Se creează...'" in html or "Se creează" in html, "Should show loading text during submission"
# Check that original text is stored for restoration
assert "originalText = createBtn.textContent" in html, "Should store original button text"
print("✓ Shows loading state on submit (button disabled)")
def test_success_behavior():
"""Test behavior on successful habit creation"""
with open('dashboard/habits.html', 'r') as f:
html = f.read()
# Check for success handling block
assert "if (response.ok)" in html, "Should check for successful response"
# Check that modal closes on success
assert "hideHabitModal()" in html, "Modal should close on success"
# Check that habits list is refreshed
assert "loadHabits()" in html, "Should reload habits list on success"
# Check that success toast is shown
assert "showToast(" in html and "succes" in html, "Should show success toast"
print("✓ On success: modal closes, list refreshes, new habit appears")
def test_error_behavior():
"""Test behavior on error (modal stays open, shows error)"""
with open('dashboard/habits.html', 'r') as f:
html = f.read()
# Check for error handling in response
assert "else {" in html or "!response.ok" in html, "Should handle error responses"
# Check for catch block for network errors
assert "catch (error)" in html, "Should catch network errors"
# Check that error toast is shown
assert "showToast('Eroare" in html, "Should show error toast on failure"
# Check that button is re-enabled on error (so modal stays usable)
createHabit_block = html[html.find('async function createHabit()'):html.find('async function createHabit()') + 2000]
# Count occurrences of button re-enable in error paths
error_section = html[html.find('} else {'):html.find('} catch (error)') + 500]
assert 'createBtn.disabled = false' in error_section, "Button should be re-enabled on error"
assert 'createBtn.textContent = originalText' in error_section, "Button text should be restored on error"
print("✓ On error: shows error message, modal stays open (button re-enabled)")
def test_input_cleared_after_success():
"""Test that input field is cleared after successful creation"""
with open('dashboard/habits.html', 'r') as f:
html = f.read()
# Find the success block
success_section = html[html.find('if (response.ok)'):html.find('if (response.ok)') + 500]
# Check that nameInput.value is cleared
assert "nameInput.value = ''" in success_section or 'nameInput.value = ""' in success_section, \
"Input field should be cleared after successful creation"
print("✓ Input field cleared after successful creation")
def test_form_validation_still_works():
"""Test that existing form validation is still in place"""
with open('dashboard/habits.html', 'r') as f:
html = f.read()
# Check that empty name validation exists
assert "if (!name)" in html, "Should validate for empty name"
assert "name = nameInput.value.trim()" in html, "Should trim name before validation"
# Check that create button is disabled when name is empty
assert "nameInput.addEventListener('input'" in html, "Should listen to input changes"
assert "createBtn.disabled = name.length === 0" in html, "Button should be disabled when name is empty"
print("✓ Form validation still works (empty name check)")
def test_modal_reset_on_open():
"""Test that modal resets form when opened"""
with open('dashboard/habits.html', 'r') as f:
html = f.read()
# Check showAddHabitModal function
assert 'function showAddHabitModal()' in html, "showAddHabitModal function should exist"
# Check that form is reset when modal opens
modal_function = html[html.find('function showAddHabitModal()'):html.find('function showAddHabitModal()') + 500]
assert "nameInput.value = ''" in modal_function or 'nameInput.value = ""' in modal_function, \
"Should clear name input when opening modal"
print("✓ Modal resets form when opened")
def test_enter_key_submission():
"""Test that Enter key can submit the form"""
with open('dashboard/habits.html', 'r') as f:
html = f.read()
# Check for Enter key handler
assert "addEventListener('keypress'" in html, "Should listen for keypress events"
assert "e.key === 'Enter'" in html, "Should check for Enter key"
assert "!createBtn.disabled" in html, "Should check if button is enabled before submitting"
assert "createHabit()" in html, "Should call createHabit on Enter"
print("✓ Enter key submits form when button is enabled")
def test_all_acceptance_criteria():
"""Summary test - verify all acceptance criteria are met"""
with open('dashboard/habits.html', 'r') as f:
html = f.read()
checks = [
("'/api/habits'" in html and "method: 'POST'" in html and "body: JSON.stringify({ name, frequency })" in html,
"1. Form submit calls POST /api/habits with name and frequency"),
("createBtn.disabled = true" in html and "Se creează" in html,
"2. Shows loading state on submit (button disabled)"),
("hideHabitModal()" in html and "loadHabits()" in html and "response.ok" in html,
"3. On success: modal closes, list refreshes, new habit appears"),
("catch (error)" in html and "createBtn.disabled = false" in html,
"4. On error: shows error message, modal stays open"),
("nameInput.value = ''" in html or 'nameInput.value = ""' in html,
"5. Input field cleared after successful creation"),
(True, "6. Tests for form submission pass (this test!)")
]
all_pass = True
for condition, description in checks:
status = "" if condition else ""
print(f" {status} {description}")
if not condition:
all_pass = False
assert all_pass, "Not all acceptance criteria are met"
print("\n✓ All acceptance criteria verified!")
if __name__ == '__main__':
try:
test_form_submit_api_call()
test_loading_state_on_submit()
test_success_behavior()
test_error_behavior()
test_input_cleared_after_success()
test_form_validation_still_works()
test_modal_reset_on_open()
test_enter_key_submission()
test_all_acceptance_criteria()
print("\n✅ All Story 11.0 tests passed!")
sys.exit(0)
except AssertionError as e:
print(f"\n❌ Test failed: {e}", file=sys.stderr)
sys.exit(1)
except Exception as e:
print(f"\n❌ Unexpected error: {e}", file=sys.stderr)
sys.exit(1)