@zeix/ui-element
Version:
UIElement - a HTML-first library for reactive Web Components
1,277 lines (1,124 loc) • 33.5 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Todo App Component Tests</title>
</head>
<body>
<!-- Test fixtures -->
<module-todo id="test1">
<form action="#">
<form-textbox>
<label for="add-todo1">What needs to be done?</label>
<div class="input">
<input id="add-todo1" type="text" value="" required />
</div>
</form-textbox>
<basic-button class="submit">
<button type="submit" class="constructive" disabled>
Add Todo
</button>
</basic-button>
</form>
<ol filter="all"></ol>
<template>
<li>
<form-checkbox class="todo">
<label>
<input type="checkbox" class="visually-hidden" />
<span class="label"><slot></slot></span>
</label>
</form-checkbox>
<basic-button class="delete">
<button type="button" class="destructive small">
Delete
</button>
</basic-button>
</li>
</template>
<footer>
<div class="todo-count">
<p class="all-done">Well done, all done!</p>
<p class="remaining">
<span class="count"></span>
<span class="singular">task</span>
<span class="plural">tasks</span>
remaining
</p>
</div>
<form-radiogroup value="all" class="split-button">
<fieldset>
<legend class="visually-hidden">Filter</legend>
<label class="selected">
<input
type="radio"
class="visually-hidden"
name="filter"
value="all"
checked
/>
<span>All</span>
</label>
<label>
<input
type="radio"
class="visually-hidden"
name="filter"
value="active"
/>
<span>Active</span>
</label>
<label>
<input
type="radio"
class="visually-hidden"
name="filter"
value="completed"
/>
<span>Completed</span>
</label>
</fieldset>
</form-radiogroup>
<basic-button class="clear-completed">
<button type="button" class="destructive">
<span class="label">Clear Completed</span>
<span class="badge"></span>
</button>
</basic-button>
</footer>
</module-todo>
<module-todo id="test2">
<form action="#">
<form-textbox>
<label for="add-todo2">What needs to be done?</label>
<div class="input">
<input id="add-todo2" type="text" value="" required />
</div>
</form-textbox>
<basic-button class="submit">
<button type="submit" class="constructive" disabled>
Add Todo
</button>
</basic-button>
</form>
<ol filter="all"></ol>
<template>
<li>
<form-checkbox class="todo">
<label>
<input type="checkbox" class="visually-hidden" />
<span class="label"><slot></slot></span>
</label>
</form-checkbox>
<basic-button class="delete">
<button type="button" class="destructive small">
Delete
</button>
</basic-button>
</li>
</template>
<footer>
<div class="todo-count">
<p class="all-done">Well done, all done!</p>
<p class="remaining">
<span class="count"></span>
<span class="singular">task</span>
<span class="plural">tasks</span>
remaining
</p>
</div>
<form-radiogroup value="all" class="split-button">
<fieldset>
<legend class="visually-hidden">Filter</legend>
<label class="selected">
<input
type="radio"
class="visually-hidden"
name="filter"
value="all"
checked
/>
<span>All</span>
</label>
<label>
<input
type="radio"
class="visually-hidden"
name="filter"
value="active"
/>
<span>Active</span>
</label>
<label>
<input
type="radio"
class="visually-hidden"
name="filter"
value="completed"
/>
<span>Completed</span>
</label>
</fieldset>
</form-radiogroup>
<basic-button class="clear-completed">
<button type="button" class="destructive">
<span class="label">Clear Completed</span>
<span class="badge"></span>
</button>
</basic-button>
</footer>
</module-todo>
<script type="module">
import { runTests } from '@web/test-runner-mocha'
import { assert } from '@esm-bundle/chai'
import '../../../docs/assets/main.js' // Built components bundle
const wait = ms => new Promise(resolve => setTimeout(resolve, ms))
const animationFrame = () => new Promise(requestAnimationFrame)
const microtask = () => new Promise(queueMicrotask)
const tick = async () => {
await animationFrame() // Wait for effects to execute
await microtask() // Wait for DOM to reflect changes
}
// Helper to reset module-todo component state
const resetTodoApp = async el => {
// Clear all todos
const list = el.querySelector('ol')
if (list) {
list.innerHTML = ''
}
// Reset input
const input = el.querySelector('form-textbox input')
const textbox = el.querySelector('form-textbox')
if (input && textbox) {
input.value = ''
textbox.value = ''
textbox.length = 0
input.dispatchEvent(new Event('input', { bubbles: true }))
input.dispatchEvent(new Event('change', { bubbles: true }))
}
// Reset filter to 'all'
const radiogroup = el.querySelector('form-radiogroup')
if (radiogroup) {
radiogroup.value = 'all'
const allRadio =
radiogroup.querySelector('input[value="all"]')
if (allRadio) {
allRadio.checked = true
allRadio.dispatchEvent(
new Event('change', { bubbles: true }),
)
}
}
await tick()
}
// Helper to add a todo item
const addTodo = async (el, text) => {
const input = el.querySelector('form-textbox input')
const textbox = el.querySelector('form-textbox')
const form = el.querySelector('form')
// Set native input value and trigger events to sync component
input.value = text
input.dispatchEvent(new Event('input', { bubbles: true }))
input.dispatchEvent(new Event('change', { bubbles: true }))
await tick()
// Submit the form
const submitEvent = new Event('submit', {
bubbles: true,
cancelable: true,
})
form.dispatchEvent(submitEvent)
await tick()
// Wait for microtask in module-todo to complete
await microtask()
await tick()
}
// Helper to get todo items
const getTodos = el => {
return Array.from(el.querySelectorAll('ol li'))
}
// Helper to get active todos
const getActiveTodos = el => {
return getTodos(el).filter(li => {
const checkbox = li.querySelector('form-checkbox input')
return checkbox && !checkbox.checked
})
}
// Helper to get completed todos
const getCompletedTodos = el => {
return getTodos(el).filter(li => {
const checkbox = li.querySelector('form-checkbox input')
return checkbox && checkbox.checked
})
}
// Helper to toggle todo completion
const toggleTodo = async li => {
const checkbox = li.querySelector('form-checkbox input')
if (checkbox) {
checkbox.checked = !checkbox.checked
checkbox.dispatchEvent(
new Event('change', { bubbles: true }),
)
await tick()
}
}
// Helper to delete todo
const deleteTodo = async li => {
const deleteButton = li.querySelector(
'basic-button.delete button',
)
if (deleteButton) {
deleteButton.click()
await tick()
}
}
runTests(() => {
describe('Todo App Component', () => {
beforeEach(async () => {
// Reset all test components before each test
const testIds = ['test1', 'test2']
for (const id of testIds) {
const el = document.getElementById(id)
if (el) await resetTodoApp(el)
}
})
it('should verify component exists and has expected structure', async () => {
const el = document.getElementById('test1')
assert.isNotNull(el, 'Todo app component should exist')
assert.equal(el.tagName.toLowerCase(), 'module-todo')
// Check required elements exist
assert.isNotNull(
el.querySelector('form'),
'Form should exist',
)
assert.isNotNull(
el.querySelector('form-textbox'),
'Input textbox should exist',
)
assert.isNotNull(
el.querySelector('basic-button.submit'),
'Submit button should exist',
)
assert.isNotNull(
el.querySelector('ol'),
'Todo list should exist',
)
assert.isNotNull(
el.querySelector('template'),
'Template should exist',
)
assert.isNotNull(
el.querySelector('form-radiogroup'),
'Filter radiogroup should exist',
)
assert.isNotNull(
el.querySelector('basic-button.clear-completed'),
'Clear completed button should exist',
)
})
it('should initialize with correct default state', async () => {
const el = document.getElementById('test1')
await tick()
// Should have empty active and completed arrays
assert.isArray(
el.active,
'Active property should be an array',
)
assert.isArray(
el.completed,
'Completed property should be an array',
)
assert.equal(
el.active.length,
0,
'Should start with no active todos',
)
assert.equal(
el.completed.length,
0,
'Should start with no completed todos',
)
// Submit button should be disabled
const submitButton = el.querySelector(
'basic-button.submit',
)
assert.isTrue(
submitButton.disabled,
'Submit button should be disabled initially',
)
// Clear completed button should be disabled
const clearButton = el.querySelector(
'basic-button.clear-completed',
)
assert.isTrue(
clearButton.disabled,
'Clear completed button should be disabled initially',
)
// Count should be 0
const count = el.querySelector('.count')
assert.equal(
count.textContent,
'0',
'Count should be 0',
)
// All done message should be visible
const allDone = el.querySelector('.all-done')
assert.isFalse(
allDone.hidden,
'All done message should be visible',
)
// Remaining message should be hidden
const remaining = el.querySelector('.remaining')
assert.isTrue(
remaining.hidden,
'Remaining message should be hidden',
)
})
it('should enable submit button when input has value', async () => {
const el = document.getElementById('test1')
const input = el.querySelector('form-textbox input')
const textbox = el.querySelector('form-textbox')
const submitButton = el.querySelector(
'basic-button.submit',
)
// Initially disabled
assert.isTrue(
submitButton.disabled,
'Submit button should be disabled initially',
)
// Type in input
input.value = 'Test todo'
input.dispatchEvent(
new Event('input', { bubbles: true }),
)
await tick()
// Should be enabled
assert.isFalse(
submitButton.disabled,
'Submit button should be enabled with input',
)
// Clear input
input.value = ''
input.dispatchEvent(
new Event('input', { bubbles: true }),
)
await tick()
// Should be disabled again
assert.isTrue(
submitButton.disabled,
'Submit button should be disabled when input is empty',
)
})
it('should maintain reactive submit button behavior with getSignal()', async () => {
const el = document.getElementById('test1')
const textbox = el.querySelector('form-textbox')
const input = el.querySelector('form-textbox input')
const submitButton = el.querySelector(
'basic-button.submit',
)
// Verify reactive behavior works initially
assert.isTrue(
submitButton.disabled,
'Submit button should be disabled initially',
)
// Type in input
input.value = 'test'
input.dispatchEvent(
new Event('input', { bubbles: true }),
)
await tick()
assert.isFalse(
submitButton.disabled,
'Submit button should be enabled with input',
)
// Clear input by setting component property directly
textbox.length = 0
await tick()
// Should be disabled when length is 0
assert.isTrue(
submitButton.disabled,
'Submit button should be disabled when textbox.length = 0',
)
// Set length back to test reactive dependency
textbox.length = 5
await tick()
// Should be enabled when length > 0
assert.isFalse(
submitButton.disabled,
'Submit button should be enabled when textbox.length > 0',
)
})
it('should handle component timing with receive() function', async () => {
const el = document.getElementById('test1')
const radiogroup = el.querySelector('form-radiogroup')
const list = el.querySelector('ol')
// Initially should have 'all' filter (fallback value)
assert.equal(
list.getAttribute('filter'),
'all',
'Filter should start with fallback value "all"',
)
// Change radiogroup value and verify reactive update
const activeRadio = radiogroup.querySelector(
'input[value="active"]',
)
const allRadio =
radiogroup.querySelector('input[value="all"]')
// Simulate radio button change
allRadio.checked = false
activeRadio.checked = true
activeRadio.dispatchEvent(
new Event('change', { bubbles: true }),
)
await tick()
// Should reactively update to 'active'
assert.equal(
list.getAttribute('filter'),
'active',
'Filter should update to "active" via receive() function',
)
// Change back to 'all'
activeRadio.checked = false
allRadio.checked = true
allRadio.dispatchEvent(
new Event('change', { bubbles: true }),
)
await tick()
// Should reactively update back to 'all'
assert.equal(
list.getAttribute('filter'),
'all',
'Filter should update back to "all"',
)
})
it('should add new todo item when form is submitted', async () => {
const el = document.getElementById('test1')
await addTodo(el, 'Test todo item')
const todos = getTodos(el)
assert.equal(
todos.length,
1,
'Should have one todo item',
)
const todoText =
todos[0].querySelector('.label').textContent
assert.equal(
todoText,
'Test todo item',
'Todo should have correct text',
)
// Input should be cleared (both native input and component)
const input = el.querySelector('form-textbox input')
const textbox = el.querySelector('form-textbox')
assert.equal(
textbox.value,
'',
'Component value should be cleared after adding todo',
)
})
it('should update active todos array when todo is added', async () => {
const el = document.getElementById('test1')
await addTodo(el, 'First todo')
await tick()
assert.equal(
el.active.length,
1,
'Should have one active todo',
)
assert.equal(
el.completed.length,
0,
'Should have no completed todos',
)
await addTodo(el, 'Second todo')
await tick()
assert.equal(
el.active.length,
2,
'Should have two active todos',
)
assert.equal(
el.completed.length,
0,
'Should have no completed todos',
)
})
it('should update count display correctly', async () => {
const el = document.getElementById('test1')
const count = el.querySelector('.count')
const singular = el.querySelector('.singular')
const plural = el.querySelector('.plural')
const remaining = el.querySelector('.remaining')
const allDone = el.querySelector('.all-done')
// Initially
assert.equal(
count.textContent,
'0',
'Count should be 0 initially',
)
assert.isTrue(
remaining.hidden,
'Remaining should be hidden initially',
)
assert.isFalse(
allDone.hidden,
'All done should be visible initially',
)
// Add one todo
await addTodo(el, 'First todo')
await tick()
assert.equal(
count.textContent,
'1',
'Count should be 1',
)
assert.isFalse(
remaining.hidden,
'Remaining should be visible',
)
assert.isTrue(
allDone.hidden,
'All done should be hidden',
)
assert.isFalse(
singular.hidden,
'Singular should be visible for 1 task',
)
assert.isTrue(
plural.hidden,
'Plural should be hidden for 1 task',
)
// Add second todo
await addTodo(el, 'Second todo')
await tick()
assert.equal(
count.textContent,
'2',
'Count should be 2',
)
assert.isTrue(
singular.hidden,
'Singular should be hidden for 2 tasks',
)
assert.isFalse(
plural.hidden,
'Plural should be visible for 2 tasks',
)
})
it('should handle todo completion correctly', async () => {
const el = document.getElementById('test1')
await addTodo(el, 'Test todo')
await tick()
const todos = getTodos(el)
const firstTodo = todos[0]
// Initially active
assert.equal(
el.active.length,
1,
'Should have one active todo',
)
assert.equal(
el.completed.length,
0,
'Should have no completed todos',
)
// Complete the todo
await toggleTodo(firstTodo)
await tick()
assert.equal(
el.active.length,
0,
'Should have no active todos',
)
assert.equal(
el.completed.length,
1,
'Should have one completed todo',
)
// Uncomplete the todo
await toggleTodo(firstTodo)
await tick()
assert.equal(
el.active.length,
1,
'Should have one active todo again',
)
assert.equal(
el.completed.length,
0,
'Should have no completed todos again',
)
})
it('should enable clear completed button when there are completed todos', async () => {
const el = document.getElementById('test1')
const clearButton = el.querySelector(
'basic-button.clear-completed',
)
// Initially disabled
assert.isTrue(
clearButton.disabled,
'Clear button should be disabled initially',
)
// Add and complete a todo
await addTodo(el, 'Test todo')
const todos = getTodos(el)
await toggleTodo(todos[0])
await tick()
// Should be enabled
assert.isFalse(
clearButton.disabled,
'Clear button should be enabled with completed todos',
)
// Should show badge with count
await tick()
const badge = clearButton.querySelector('.badge')
assert.equal(
badge.textContent,
'1',
'Badge should show completed count',
)
})
it('should clear completed todos when clear button is clicked', async () => {
const el = document.getElementById('test1')
// Add multiple todos
await addTodo(el, 'First todo')
await addTodo(el, 'Second todo')
await addTodo(el, 'Third todo')
let todos = getTodos(el)
assert.equal(todos.length, 3, 'Should have 3 todos')
// Complete first and third todos
await toggleTodo(todos[0])
await toggleTodo(todos[2])
await tick()
assert.equal(
el.active.length,
1,
'Should have 1 active todo',
)
assert.equal(
el.completed.length,
2,
'Should have 2 completed todos',
)
// Click clear completed button
const clearButton = el.querySelector(
'basic-button.clear-completed button',
)
clearButton.click()
await tick()
todos = getTodos(el)
assert.equal(
todos.length,
1,
'Should have 1 todo remaining',
)
assert.equal(
el.active.length,
1,
'Should have 1 active todo',
)
assert.equal(
el.completed.length,
0,
'Should have 0 completed todos',
)
})
it('should delete individual todos when delete button is clicked', async () => {
const el = document.getElementById('test1')
await addTodo(el, 'First todo')
await addTodo(el, 'Second todo')
let todos = getTodos(el)
assert.equal(todos.length, 2, 'Should have 2 todos')
// Delete first todo
await deleteTodo(todos[0])
todos = getTodos(el)
assert.equal(
todos.length,
1,
'Should have 1 todo remaining',
)
const remainingText =
todos[0].querySelector('.label').textContent
assert.equal(
remainingText,
'Second todo',
'Remaining todo should be the second one',
)
})
it('should update filter attribute on list when filter changes', async () => {
const el = document.getElementById('test1')
const list = el.querySelector('ol')
const radiogroup = el.querySelector('form-radiogroup')
// Initially 'all'
assert.equal(
list.getAttribute('filter'),
'all',
'Filter should be "all" initially',
)
// Change to 'active'
const activeRadio = radiogroup.querySelector(
'input[value="active"]',
)
activeRadio.checked = true
radiogroup.value = 'active'
activeRadio.dispatchEvent(
new Event('change', { bubbles: true }),
)
await tick()
assert.equal(
list.getAttribute('filter'),
'active',
'Filter should be "active"',
)
// Change to 'completed'
const completedRadio = radiogroup.querySelector(
'input[value="completed"]',
)
completedRadio.checked = true
radiogroup.value = 'completed'
completedRadio.dispatchEvent(
new Event('change', { bubbles: true }),
)
await tick()
assert.equal(
list.getAttribute('filter'),
'completed',
'Filter should be "completed"',
)
})
it('should update filter attribute reactively when only radio button changes', async () => {
const el = document.getElementById('test1')
const list = el.querySelector('ol')
const radiogroup = el.querySelector('form-radiogroup')
// Initially 'all'
assert.equal(
list.getAttribute('filter'),
'all',
'Filter should be "all" initially',
)
// Change to 'active' - simulate real user interaction (only radio button change event)
const activeRadio = radiogroup.querySelector(
'input[value="active"]',
)
const allRadio =
radiogroup.querySelector('input[value="all"]')
// Uncheck current and check new (as browser would do)
allRadio.checked = false
activeRadio.checked = true
// Only fire the change event on the radio button (no manual radiogroup.value setting)
activeRadio.dispatchEvent(
new Event('change', { bubbles: true }),
)
await tick()
// This should work but will likely fail due to reactive dependency issue
assert.equal(
list.getAttribute('filter'),
'active',
'Filter should reactively update to "active"',
)
// Change to 'completed' - same pattern
const completedRadio = radiogroup.querySelector(
'input[value="completed"]',
)
activeRadio.checked = false
completedRadio.checked = true
completedRadio.dispatchEvent(
new Event('change', { bubbles: true }),
)
await tick()
assert.equal(
list.getAttribute('filter'),
'completed',
'Filter should reactively update to "completed"',
)
})
it('should test reactive dependency mechanism for filter', async () => {
const el = document.getElementById('test1')
const list = el.querySelector('ol')
const radiogroup = el.querySelector('form-radiogroup')
// Track if effect re-runs when radiogroup.value changes
let effectRunCount = 0
const originalSetAttribute = list.setAttribute
list.setAttribute = function (...args) {
if (args[0] === 'filter') effectRunCount++
return originalSetAttribute.apply(this, args)
}
// Initially 'all'
assert.equal(
list.getAttribute('filter'),
'all',
'Filter should be "all" initially',
)
// Test 1: Set radiogroup value directly (should trigger reactive update if dependency exists)
radiogroup.value = 'active'
await tick()
// Test 2: Simulate real browser behavior
const activeRadio = radiogroup.querySelector(
'input[value="active"]',
)
const allRadio =
radiogroup.querySelector('input[value="all"]')
// Reset state
allRadio.checked = true
activeRadio.checked = false
radiogroup.value = 'all'
await tick()
// Now change via proper radio button interaction
allRadio.checked = false
activeRadio.checked = true
activeRadio.dispatchEvent(
new Event('change', { bubbles: true }),
)
await tick()
// Restore original setAttribute
list.setAttribute = originalSetAttribute
// The real test: did the reactive system properly track the dependency?
assert.equal(
list.getAttribute('filter'),
'active',
'Filter should be "active" after event',
)
})
it('should prevent form submission with empty input', async () => {
const el = document.getElementById('test1')
const form = el.querySelector('form')
const input = el.querySelector('form-textbox input')
// Try to submit with empty input
input.value = ''
form.dispatchEvent(
new Event('submit', {
bubbles: true,
cancelable: true,
}),
)
await tick()
const todos = getTodos(el)
assert.equal(
todos.length,
0,
'Should not add todo with empty input',
)
})
it('should trim whitespace from todo input', async () => {
const el = document.getElementById('test1')
await addTodo(el, ' Whitespace todo ')
const todos = getTodos(el)
const todoText =
todos[0].querySelector('.label').textContent
assert.equal(
todoText,
'Whitespace todo',
'Todo text should be trimmed',
)
})
it('should handle multiple todos with mixed states', async () => {
const el = document.getElementById('test1')
// Add multiple todos
await addTodo(el, 'Todo 1')
await addTodo(el, 'Todo 2')
await addTodo(el, 'Todo 3')
await addTodo(el, 'Todo 4')
let todos = getTodos(el)
assert.equal(todos.length, 4, 'Should have 4 todos')
// Complete some todos
await toggleTodo(todos[1]) // Complete Todo 2
await toggleTodo(todos[3]) // Complete Todo 4
await tick()
assert.equal(
el.active.length,
2,
'Should have 2 active todos',
)
assert.equal(
el.completed.length,
2,
'Should have 2 completed todos',
)
// Check count display
const count = el.querySelector('.count')
assert.equal(
count.textContent,
'2',
'Count should show 2 active todos',
)
// Check clear completed button
const clearButton = el.querySelector(
'basic-button.clear-completed',
)
assert.isFalse(
clearButton.disabled,
'Clear button should be enabled',
)
// Wait for badge to update
await tick()
const badge = clearButton.querySelector('.badge')
assert.equal(
badge.textContent,
'2',
'Badge should show 2 completed todos',
)
})
it('should handle rapid todo additions', async () => {
const el = document.getElementById('test1')
// Add multiple todos sequentially to avoid race conditions
for (let i = 1; i <= 5; i++) {
await addTodo(el, `Todo ${i}`)
}
const todos = getTodos(el)
assert.equal(todos.length, 5, 'Should have 5 todos')
assert.equal(
el.active.length,
5,
'Should have 5 active todos',
)
assert.equal(
el.completed.length,
0,
'Should have 0 completed todos',
)
})
it('should handle all todos completed scenario', async () => {
const el = document.getElementById('test1')
const allDone = el.querySelector('.all-done')
const remaining = el.querySelector('.remaining')
// Add and complete all todos
await addTodo(el, 'Todo 1')
await addTodo(el, 'Todo 2')
const todos = getTodos(el)
await toggleTodo(todos[0])
await toggleTodo(todos[1])
await tick()
assert.equal(
el.active.length,
0,
'Should have 0 active todos',
)
assert.equal(
el.completed.length,
2,
'Should have 2 completed todos',
)
// UI should show all done
assert.isFalse(
allDone.hidden,
'All done message should be visible',
)
assert.isTrue(
remaining.hidden,
'Remaining message should be hidden',
)
})
it('should maintain component state across multiple operations', async () => {
const el = document.getElementById('test2') // Use second test instance
// Complex workflow
await addTodo(el, 'Task 1')
await addTodo(el, 'Task 2')
await addTodo(el, 'Task 3')
let todos = getTodos(el)
// Complete first task
await toggleTodo(todos[0])
await tick()
// Delete second task
await deleteTodo(todos[1])
await tick()
// Add another task
await addTodo(el, 'Task 4')
// Final state check
todos = getTodos(el)
assert.equal(
todos.length,
3,
'Should have 3 todos total',
)
assert.equal(
el.active.length,
2,
'Should have 2 active todos',
)
assert.equal(
el.completed.length,
1,
'Should have 1 completed todo',
)
// Verify the remaining tasks are correct
const todoTexts = todos.map(
todo => todo.querySelector('.label').textContent,
)
assert.include(
todoTexts,
'Task 1',
'Should include Task 1',
)
assert.include(
todoTexts,
'Task 3',
'Should include Task 3',
)
assert.include(
todoTexts,
'Task 4',
'Should include Task 4',
)
assert.notInclude(
todoTexts,
'Task 2',
'Should not include deleted Task 2',
)
})
it('should clear input field when input.clear() is called', async () => {
const app = document.getElementById('test1')
const input = app.querySelector('form-textbox')
const inputEl = input.querySelector('input')
// Add some content to the input
inputEl.focus()
inputEl.value = 'Test todo item'
inputEl.dispatchEvent(
new Event('input', { bubbles: true }),
)
inputEl.dispatchEvent(
new Event('change', { bubbles: true }),
)
await tick()
// Verify input has content
assert.equal(input.value, 'Test todo item')
assert.equal(input.length, 14)
assert.equal(inputEl.value, 'Test todo item')
// Call clear method
input.clear()
await tick()
await tick() // Extra wait for reactive updates
// Verify everything is cleared
assert.equal(input.value, '')
assert.equal(input.length, 0)
assert.equal(inputEl.value, '')
// Verify submit button is disabled again
const submitBtn = app.querySelector('.submit button')
await tick() // Extra wait for submit button reactivity
assert.isTrue(
submitBtn.disabled,
'Submit button should be disabled when input is empty',
)
})
it('should clear input after successful todo submission', async () => {
const app = document.getElementById('test1')
const input = app.querySelector('form-textbox')
const inputEl = input.querySelector('input')
const form = app.querySelector('form')
// Add a todo
await addTodo(app, 'Test todo for clearing')
// Verify input was cleared automatically after submission
assert.equal(input.value, '')
assert.equal(input.length, 0)
assert.equal(inputEl.value, '')
// Verify the todo was actually added
const todos = getTodos(app)
assert.equal(todos.length, 1)
assert.include(
todos[0].textContent,
'Test todo for clearing',
)
})
})
})
</script>
</body>
</html>