#!/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)