feat: US-009 - Frontend - Edit habit modal
This commit is contained in:
@@ -853,6 +853,15 @@
|
|||||||
function showAddHabitModal() {
|
function showAddHabitModal() {
|
||||||
const modal = document.getElementById('habitModal');
|
const modal = document.getElementById('habitModal');
|
||||||
const form = document.getElementById('habitForm');
|
const form = document.getElementById('habitForm');
|
||||||
|
const modalTitle = document.querySelector('.modal-title');
|
||||||
|
const submitBtnText = document.getElementById('submitBtnText');
|
||||||
|
|
||||||
|
// Reset editing state
|
||||||
|
editingHabitId = null;
|
||||||
|
|
||||||
|
// Reset modal title and button text to create mode
|
||||||
|
modalTitle.textContent = 'Add Habit';
|
||||||
|
submitBtnText.textContent = 'Create Habit';
|
||||||
|
|
||||||
// Reset form
|
// Reset form
|
||||||
form.reset();
|
form.reset();
|
||||||
@@ -877,6 +886,9 @@
|
|||||||
function closeHabitModal() {
|
function closeHabitModal() {
|
||||||
const modal = document.getElementById('habitModal');
|
const modal = document.getElementById('habitModal');
|
||||||
modal.classList.remove('active');
|
modal.classList.remove('active');
|
||||||
|
|
||||||
|
// Reset editing state
|
||||||
|
editingHabitId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close modal when clicking outside
|
// Close modal when clicking outside
|
||||||
@@ -1004,6 +1016,9 @@
|
|||||||
const submitBtn = document.getElementById('submitHabitBtn');
|
const submitBtn = document.getElementById('submitHabitBtn');
|
||||||
const submitBtnText = document.getElementById('submitBtnText');
|
const submitBtnText = document.getElementById('submitBtnText');
|
||||||
|
|
||||||
|
// Determine if we're creating or editing
|
||||||
|
const isEditing = editingHabitId !== null;
|
||||||
|
|
||||||
// Get form values
|
// Get form values
|
||||||
const name = document.getElementById('habitName').value.trim();
|
const name = document.getElementById('habitName').value.trim();
|
||||||
const category = document.getElementById('habitCategory').value;
|
const category = document.getElementById('habitCategory').value;
|
||||||
@@ -1061,11 +1076,14 @@
|
|||||||
|
|
||||||
// Show loading state
|
// Show loading state
|
||||||
submitBtn.disabled = true;
|
submitBtn.disabled = true;
|
||||||
submitBtnText.textContent = 'Creating...';
|
submitBtnText.textContent = isEditing ? 'Saving...' : 'Creating...';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/echo/api/habits', {
|
const url = isEditing ? `/echo/api/habits/${editingHabitId}` : '/echo/api/habits';
|
||||||
method: 'POST',
|
const method = isEditing ? 'PUT' : 'POST';
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: method,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
@@ -1078,16 +1096,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Success
|
// Success
|
||||||
showToast('Habit created successfully!', 'success');
|
showToast(isEditing ? 'Habit updated!' : 'Habit created successfully!', 'success');
|
||||||
closeHabitModal();
|
closeHabitModal();
|
||||||
await loadHabits();
|
await loadHabits();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to create habit:', error);
|
console.error(`Failed to ${isEditing ? 'update' : 'create'} habit:`, error);
|
||||||
showToast('Failed to create habit: ' + error.message, 'error');
|
showToast(`Failed to ${isEditing ? 'update' : 'create'} habit: ` + error.message, 'error');
|
||||||
} finally {
|
} finally {
|
||||||
submitBtn.disabled = false;
|
submitBtn.disabled = false;
|
||||||
submitBtnText.textContent = 'Create Habit';
|
submitBtnText.textContent = isEditing ? 'Save Changes' : 'Create Habit';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1108,9 +1126,71 @@
|
|||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show edit habit modal (placeholder)
|
// Show edit habit modal
|
||||||
|
let editingHabitId = null; // Track which habit we're editing
|
||||||
|
|
||||||
function showEditHabitModal(habitId) {
|
function showEditHabitModal(habitId) {
|
||||||
alert('Edit Habit modal - coming in next story!');
|
const habit = habits.find(h => h.id === habitId);
|
||||||
|
if (!habit) {
|
||||||
|
showToast('Habit not found', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
editingHabitId = habitId;
|
||||||
|
|
||||||
|
const modal = document.getElementById('habitModal');
|
||||||
|
const form = document.getElementById('habitForm');
|
||||||
|
const modalTitle = document.querySelector('.modal-title');
|
||||||
|
const submitBtn = document.getElementById('submitHabitBtn');
|
||||||
|
const submitBtnText = document.getElementById('submitBtnText');
|
||||||
|
|
||||||
|
// Change modal title and button text
|
||||||
|
modalTitle.textContent = 'Edit Habit';
|
||||||
|
submitBtnText.textContent = 'Save Changes';
|
||||||
|
|
||||||
|
// Pre-populate form fields
|
||||||
|
document.getElementById('habitName').value = habit.name;
|
||||||
|
document.getElementById('habitCategory').value = habit.category || 'health';
|
||||||
|
document.getElementById('habitPriority').value = habit.priority || 3;
|
||||||
|
document.getElementById('habitNotes').value = habit.notes || '';
|
||||||
|
document.getElementById('frequencyType').value = habit.frequency?.type || 'daily';
|
||||||
|
document.getElementById('reminderTime').value = habit.reminderTime || '';
|
||||||
|
|
||||||
|
// Set selected color and icon
|
||||||
|
selectedColor = habit.color || '#3B82F6';
|
||||||
|
selectedIcon = habit.icon || 'dumbbell';
|
||||||
|
|
||||||
|
// Initialize color picker with current selection
|
||||||
|
initColorPicker();
|
||||||
|
|
||||||
|
// Initialize icon picker with current selection
|
||||||
|
initIconPicker();
|
||||||
|
|
||||||
|
// Update frequency params and pre-populate
|
||||||
|
updateFrequencyParams();
|
||||||
|
|
||||||
|
// Pre-populate frequency params based on type
|
||||||
|
const frequencyType = habit.frequency?.type;
|
||||||
|
if (frequencyType === 'specific_days' && habit.frequency.days) {
|
||||||
|
const dayCheckboxes = document.querySelectorAll('input[name="day"]');
|
||||||
|
dayCheckboxes.forEach(cb => {
|
||||||
|
cb.checked = habit.frequency.days.includes(parseInt(cb.value));
|
||||||
|
});
|
||||||
|
} else if (frequencyType === 'x_per_week' && habit.frequency.count) {
|
||||||
|
const xPerWeekInput = document.getElementById('xPerWeek');
|
||||||
|
if (xPerWeekInput) {
|
||||||
|
xPerWeekInput.value = habit.frequency.count;
|
||||||
|
}
|
||||||
|
} else if (frequencyType === 'custom' && habit.frequency.interval) {
|
||||||
|
const customIntervalInput = document.getElementById('customInterval');
|
||||||
|
if (customIntervalInput) {
|
||||||
|
customIntervalInput.value = habit.frequency.interval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show modal
|
||||||
|
modal.classList.add('active');
|
||||||
|
lucide.createIcons();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete habit (placeholder)
|
// Delete habit (placeholder)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ Test suite for Habits frontend page structure and navigation
|
|||||||
Story US-006: Frontend - Page structure, layout, and navigation link
|
Story US-006: Frontend - Page structure, layout, and navigation link
|
||||||
Story US-007: Frontend - Habit card component
|
Story US-007: Frontend - Habit card component
|
||||||
Story US-008: Frontend - Create habit modal with all options
|
Story US-008: Frontend - Create habit modal with all options
|
||||||
|
Story US-009: Frontend - Edit habit modal
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
@@ -417,17 +418,18 @@ def test_client_side_validation():
|
|||||||
print("✓ Test 28: Client-side validation checks name required")
|
print("✓ Test 28: Client-side validation checks name required")
|
||||||
|
|
||||||
def test_submit_posts_to_api():
|
def test_submit_posts_to_api():
|
||||||
"""Test 29: Submit sends POST /echo/api/habits and refreshes list"""
|
"""Test 29: Submit sends POST /echo/api/habits (or PUT for edit) and refreshes list"""
|
||||||
habits_path = Path(__file__).parent.parent / 'habits.html'
|
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||||
content = habits_path.read_text()
|
content = habits_path.read_text()
|
||||||
|
|
||||||
assert 'submitHabitForm' in content, "Should have submitHabitForm function"
|
assert 'submitHabitForm' in content, "Should have submitHabitForm function"
|
||||||
|
|
||||||
submit_func = content[content.find('function submitHabitForm'):]
|
submit_func = content[content.find('function submitHabitForm'):]
|
||||||
assert "fetch('/echo/api/habits'" in submit_func or 'fetch("/echo/api/habits"' in submit_func, \
|
# Check for conditional URL and method (since US-009 added edit support)
|
||||||
"Should POST to /echo/api/habits"
|
assert ('/echo/api/habits' in submit_func), \
|
||||||
assert "'POST'" in submit_func or '"POST"' in submit_func, \
|
"Should use /echo/api/habits endpoint"
|
||||||
"Should use POST method"
|
assert ("'POST'" in submit_func or '"POST"' in submit_func or "'PUT'" in submit_func or '"PUT"' in submit_func), \
|
||||||
|
"Should use POST or PUT method"
|
||||||
assert 'JSON.stringify' in submit_func, "Should send JSON body"
|
assert 'JSON.stringify' in submit_func, "Should send JSON body"
|
||||||
assert 'loadHabits()' in submit_func, "Should refresh habit list on success"
|
assert 'loadHabits()' in submit_func, "Should refresh habit list on success"
|
||||||
|
|
||||||
@@ -508,6 +510,244 @@ def test_typecheck_us008():
|
|||||||
|
|
||||||
print("✓ Test 33: Typecheck passes (all modal functions defined)")
|
print("✓ Test 33: Typecheck passes (all modal functions defined)")
|
||||||
|
|
||||||
|
def test_edit_modal_opens_on_gear_icon():
|
||||||
|
"""Test 34: Clicking gear icon on habit card opens edit modal"""
|
||||||
|
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||||
|
content = habits_path.read_text()
|
||||||
|
|
||||||
|
# Check that gear icon exists with onclick handler
|
||||||
|
assert 'settings' in content, "Should have settings icon (gear)"
|
||||||
|
assert "showEditHabitModal" in content, "Should have showEditHabitModal function call"
|
||||||
|
|
||||||
|
# Check that showEditHabitModal function is defined and not a placeholder
|
||||||
|
assert 'function showEditHabitModal(habitId)' in content, "showEditHabitModal should be defined"
|
||||||
|
assert 'editingHabitId = habitId' in content or 'editingHabitId=habitId' in content, \
|
||||||
|
"Should set editingHabitId"
|
||||||
|
assert 'const habit = habits.find(h => h.id === habitId)' in content or \
|
||||||
|
'const habit=habits.find(h=>h.id===habitId)' in content, \
|
||||||
|
"Should find habit by ID"
|
||||||
|
|
||||||
|
print("✓ Test 34: Edit modal opens on gear icon click")
|
||||||
|
|
||||||
|
def test_edit_modal_prepopulated():
|
||||||
|
"""Test 35: Edit modal is pre-populated with all existing habit data"""
|
||||||
|
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||||
|
content = habits_path.read_text()
|
||||||
|
|
||||||
|
# Check that form fields are pre-populated
|
||||||
|
assert "getElementById('habitName').value = habit.name" in content or \
|
||||||
|
"getElementById('habitName').value=habit.name" in content, \
|
||||||
|
"Should pre-populate habit name"
|
||||||
|
assert "getElementById('habitCategory').value = habit.category" in content or \
|
||||||
|
"getElementById('habitCategory').value=habit.category" in content, \
|
||||||
|
"Should pre-populate category"
|
||||||
|
assert "getElementById('habitPriority').value = habit.priority" in content or \
|
||||||
|
"getElementById('habitPriority').value=habit.priority" in content, \
|
||||||
|
"Should pre-populate priority"
|
||||||
|
assert "getElementById('habitNotes').value = habit.notes" in content or \
|
||||||
|
"getElementById('habitNotes').value=habit.notes" in content, \
|
||||||
|
"Should pre-populate notes"
|
||||||
|
assert "getElementById('frequencyType').value = habit.frequency" in content or \
|
||||||
|
"getElementById('frequencyType').value=habit.frequency" in content, \
|
||||||
|
"Should pre-populate frequency type"
|
||||||
|
|
||||||
|
# Check color and icon selection
|
||||||
|
assert 'selectedColor = habit.color' in content or 'selectedColor=habit.color' in content, \
|
||||||
|
"Should set selectedColor from habit"
|
||||||
|
assert 'selectedIcon = habit.icon' in content or 'selectedIcon=habit.icon' in content, \
|
||||||
|
"Should set selectedIcon from habit"
|
||||||
|
|
||||||
|
print("✓ Test 35: Edit modal pre-populated with habit data")
|
||||||
|
|
||||||
|
def test_edit_modal_title_and_button():
|
||||||
|
"""Test 36: Modal title shows 'Edit Habit' and button shows 'Save Changes'"""
|
||||||
|
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||||
|
content = habits_path.read_text()
|
||||||
|
|
||||||
|
# Check that modal title is changed to Edit Habit
|
||||||
|
assert "modalTitle.textContent = 'Edit Habit'" in content or \
|
||||||
|
'modalTitle.textContent="Edit Habit"' in content or \
|
||||||
|
"modalTitle.textContent='Edit Habit'" in content, \
|
||||||
|
"Should set modal title to 'Edit Habit'"
|
||||||
|
|
||||||
|
# Check that submit button text is changed
|
||||||
|
assert "submitBtnText.textContent = 'Save Changes'" in content or \
|
||||||
|
'submitBtnText.textContent="Save Changes"' in content or \
|
||||||
|
"submitBtnText.textContent='Save Changes'" in content, \
|
||||||
|
"Should set button text to 'Save Changes'"
|
||||||
|
|
||||||
|
print("✓ Test 36: Modal title shows 'Edit Habit' and button shows 'Save Changes'")
|
||||||
|
|
||||||
|
def test_edit_modal_frequency_params():
|
||||||
|
"""Test 37: Frequency params display correctly for habit's current frequency type"""
|
||||||
|
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||||
|
content = habits_path.read_text()
|
||||||
|
|
||||||
|
# Check that updateFrequencyParams is called
|
||||||
|
assert 'updateFrequencyParams()' in content, "Should call updateFrequencyParams()"
|
||||||
|
|
||||||
|
# Check that frequency params are pre-populated for specific types
|
||||||
|
assert 'specific_days' in content and 'habit.frequency.days' in content, \
|
||||||
|
"Should handle specific_days frequency params"
|
||||||
|
assert 'x_per_week' in content and 'habit.frequency.count' in content, \
|
||||||
|
"Should handle x_per_week frequency params"
|
||||||
|
assert 'custom' in content and 'habit.frequency.interval' in content, \
|
||||||
|
"Should handle custom frequency params"
|
||||||
|
|
||||||
|
# Check that day checkboxes are pre-populated
|
||||||
|
assert 'cb.checked = habit.frequency.days.includes' in content or \
|
||||||
|
'cb.checked=habit.frequency.days.includes' in content, \
|
||||||
|
"Should pre-select days for specific_days frequency"
|
||||||
|
|
||||||
|
print("✓ Test 37: Frequency params display correctly for current frequency")
|
||||||
|
|
||||||
|
def test_edit_modal_icon_color_pickers():
|
||||||
|
"""Test 38: Icon and color pickers show current selections"""
|
||||||
|
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||||
|
content = habits_path.read_text()
|
||||||
|
|
||||||
|
# Check that pickers are initialized after setting values
|
||||||
|
assert 'initColorPicker()' in content, "Should call initColorPicker()"
|
||||||
|
assert 'initIconPicker()' in content, "Should call initIconPicker()"
|
||||||
|
|
||||||
|
# Check that selectedColor and selectedIcon are set before initialization
|
||||||
|
showEditIndex = content.find('function showEditHabitModal')
|
||||||
|
initColorIndex = content.find('initColorPicker()', showEditIndex)
|
||||||
|
selectedColorIndex = content.find('selectedColor = habit.color', showEditIndex)
|
||||||
|
|
||||||
|
assert selectedColorIndex > 0 and selectedColorIndex < initColorIndex, \
|
||||||
|
"Should set selectedColor before calling initColorPicker()"
|
||||||
|
|
||||||
|
print("✓ Test 38: Icon and color pickers show current selections")
|
||||||
|
|
||||||
|
def test_edit_modal_submit_put():
|
||||||
|
"""Test 39: Submit sends PUT /echo/api/habits/{id} and refreshes list on success"""
|
||||||
|
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||||
|
content = habits_path.read_text()
|
||||||
|
|
||||||
|
# Check that editingHabitId is tracked
|
||||||
|
assert 'let editingHabitId' in content or 'editingHabitId' in content, \
|
||||||
|
"Should track editingHabitId"
|
||||||
|
|
||||||
|
# Check that isEditing is determined
|
||||||
|
assert 'const isEditing = editingHabitId !== null' in content or \
|
||||||
|
'const isEditing=editingHabitId!==null' in content or \
|
||||||
|
'isEditing = editingHabitId !== null' in content, \
|
||||||
|
"Should determine if editing"
|
||||||
|
|
||||||
|
# Check that URL and method are conditional
|
||||||
|
assert "const url = isEditing ? `/echo/api/habits/${editingHabitId}` : '/echo/api/habits'" in content or \
|
||||||
|
'const url=isEditing?`/echo/api/habits/${editingHabitId}`' in content or \
|
||||||
|
"url = isEditing ? `/echo/api/habits/${editingHabitId}` : '/echo/api/habits'" in content, \
|
||||||
|
"URL should be conditional based on isEditing"
|
||||||
|
|
||||||
|
assert "const method = isEditing ? 'PUT' : 'POST'" in content or \
|
||||||
|
"const method=isEditing?'PUT':'POST'" in content or \
|
||||||
|
"method = isEditing ? 'PUT' : 'POST'" in content, \
|
||||||
|
"Method should be conditional (PUT for edit, POST for create)"
|
||||||
|
|
||||||
|
# Check that loadHabits is called after success
|
||||||
|
assert 'await loadHabits()' in content, "Should refresh habit list after success"
|
||||||
|
|
||||||
|
print("✓ Test 39: Submit sends PUT and refreshes list")
|
||||||
|
|
||||||
|
def test_edit_modal_toast_messages():
|
||||||
|
"""Test 40: Toast shown for success and error"""
|
||||||
|
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||||
|
content = habits_path.read_text()
|
||||||
|
|
||||||
|
# Check for conditional success message
|
||||||
|
assert "isEditing ? 'Habit updated!' : 'Habit created successfully!'" in content or \
|
||||||
|
"isEditing?'Habit updated!':'Habit created successfully!'" in content, \
|
||||||
|
"Should show different toast message for edit vs create"
|
||||||
|
|
||||||
|
# Check that error toast handles both edit and create
|
||||||
|
assert 'Failed to ${isEditing' in content or 'Failed to ' + '${isEditing' in content, \
|
||||||
|
"Error toast should be conditional"
|
||||||
|
|
||||||
|
print("✓ Test 40: Toast messages for success and error")
|
||||||
|
|
||||||
|
def test_edit_modal_add_resets_state():
|
||||||
|
"""Test 41: showAddHabitModal resets editingHabitId and modal UI"""
|
||||||
|
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||||
|
content = habits_path.read_text()
|
||||||
|
|
||||||
|
# Find showAddHabitModal function
|
||||||
|
add_modal_start = content.find('function showAddHabitModal()')
|
||||||
|
add_modal_end = content.find('function ', add_modal_start + 1)
|
||||||
|
add_modal_func = content[add_modal_start:add_modal_end]
|
||||||
|
|
||||||
|
# Check that editingHabitId is reset
|
||||||
|
assert 'editingHabitId = null' in add_modal_func or 'editingHabitId=null' in add_modal_func, \
|
||||||
|
"showAddHabitModal should reset editingHabitId to null"
|
||||||
|
|
||||||
|
# Check that modal title is reset to 'Add Habit'
|
||||||
|
assert "modalTitle.textContent = 'Add Habit'" in add_modal_func or \
|
||||||
|
'modalTitle.textContent="Add Habit"' in add_modal_func, \
|
||||||
|
"Should reset modal title to 'Add Habit'"
|
||||||
|
|
||||||
|
# Check that button text is reset to 'Create Habit'
|
||||||
|
assert "submitBtnText.textContent = 'Create Habit'" in add_modal_func or \
|
||||||
|
'submitBtnText.textContent="Create Habit"' in add_modal_func, \
|
||||||
|
"Should reset button text to 'Create Habit'"
|
||||||
|
|
||||||
|
print("✓ Test 41: showAddHabitModal resets editing state")
|
||||||
|
|
||||||
|
def test_edit_modal_close_resets_state():
|
||||||
|
"""Test 42: closeHabitModal resets editingHabitId"""
|
||||||
|
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||||
|
content = habits_path.read_text()
|
||||||
|
|
||||||
|
# Find closeHabitModal function
|
||||||
|
close_modal_start = content.find('function closeHabitModal()')
|
||||||
|
close_modal_end = content.find('function ', close_modal_start + 1)
|
||||||
|
close_modal_func = content[close_modal_start:close_modal_end]
|
||||||
|
|
||||||
|
# Check that editingHabitId is reset when closing
|
||||||
|
assert 'editingHabitId = null' in close_modal_func or 'editingHabitId=null' in close_modal_func, \
|
||||||
|
"closeHabitModal should reset editingHabitId to null"
|
||||||
|
|
||||||
|
print("✓ Test 42: closeHabitModal resets editing state")
|
||||||
|
|
||||||
|
def test_edit_modal_no_console_errors():
|
||||||
|
"""Test 43: No obvious console error sources in edit modal code"""
|
||||||
|
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||||
|
content = habits_path.read_text()
|
||||||
|
|
||||||
|
# Check for common error patterns
|
||||||
|
assert content.count('getElementById(') > 0, "Should use getElementById"
|
||||||
|
|
||||||
|
# Check that habit is validated before use
|
||||||
|
showEditIndex = content.find('function showEditHabitModal')
|
||||||
|
showEditEnd = content.find('\n }', showEditIndex + 500) # Find end of function
|
||||||
|
showEditFunc = content[showEditIndex:showEditEnd]
|
||||||
|
|
||||||
|
assert 'if (!habit)' in showEditFunc or 'if(!habit)' in showEditFunc, \
|
||||||
|
"Should check if habit exists before using it"
|
||||||
|
assert 'showToast' in showEditFunc and 'error' in showEditFunc, \
|
||||||
|
"Should show error toast if habit not found"
|
||||||
|
|
||||||
|
print("✓ Test 43: No obvious console error sources")
|
||||||
|
|
||||||
|
def test_typecheck_us009():
|
||||||
|
"""Test 44: Typecheck passes - all edit modal functions and variables defined"""
|
||||||
|
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||||
|
content = habits_path.read_text()
|
||||||
|
|
||||||
|
# Check that editingHabitId is declared
|
||||||
|
assert 'let editingHabitId' in content, "editingHabitId should be declared"
|
||||||
|
|
||||||
|
# Check that showEditHabitModal is fully implemented (not placeholder)
|
||||||
|
assert 'function showEditHabitModal(habitId)' in content, "showEditHabitModal should be defined"
|
||||||
|
assert 'alert' not in content[content.find('function showEditHabitModal'):content.find('function showEditHabitModal')+1000], \
|
||||||
|
"showEditHabitModal should not be a placeholder with alert()"
|
||||||
|
|
||||||
|
# Check that submitHabitForm handles both create and edit
|
||||||
|
assert 'const isEditing' in content or 'isEditing' in content, \
|
||||||
|
"submitHabitForm should determine if editing"
|
||||||
|
|
||||||
|
print("✓ Test 44: Typecheck passes (edit modal fully implemented)")
|
||||||
|
|
||||||
def run_all_tests():
|
def run_all_tests():
|
||||||
"""Run all tests in sequence"""
|
"""Run all tests in sequence"""
|
||||||
tests = [
|
tests = [
|
||||||
@@ -547,9 +787,21 @@ def run_all_tests():
|
|||||||
test_toast_notifications,
|
test_toast_notifications,
|
||||||
test_modal_no_console_errors,
|
test_modal_no_console_errors,
|
||||||
test_typecheck_us008,
|
test_typecheck_us008,
|
||||||
|
# US-009 tests
|
||||||
|
test_edit_modal_opens_on_gear_icon,
|
||||||
|
test_edit_modal_prepopulated,
|
||||||
|
test_edit_modal_title_and_button,
|
||||||
|
test_edit_modal_frequency_params,
|
||||||
|
test_edit_modal_icon_color_pickers,
|
||||||
|
test_edit_modal_submit_put,
|
||||||
|
test_edit_modal_toast_messages,
|
||||||
|
test_edit_modal_add_resets_state,
|
||||||
|
test_edit_modal_close_resets_state,
|
||||||
|
test_edit_modal_no_console_errors,
|
||||||
|
test_typecheck_us009,
|
||||||
]
|
]
|
||||||
|
|
||||||
print(f"\nRunning {len(tests)} frontend tests for US-006, US-007, and US-008...\n")
|
print(f"\nRunning {len(tests)} frontend tests for US-006, US-007, US-008, and US-009...\n")
|
||||||
|
|
||||||
failed = []
|
failed = []
|
||||||
for test in tests:
|
for test in tests:
|
||||||
|
|||||||
Reference in New Issue
Block a user