210 lines
8.7 KiB
Python
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)
|