Replace browser prompt()-based editing with proper Bootstrap 5 modal dialogs for testimonials, services, FAQs, and leads. This provides better UX with form validation, structured input fields, and i18n support (ES/RU) instead of raw prompt dialogs. - Add testimonialModal, serviceModal, faqModal, leadModal to admin.html - Add show*/save* methods in admin.js for each entity type - Wire leads.html 'Add lead' button to leadModal - Add modal JS modules (FAQModal, LeadModal, ServiceModal) - Add unit and e2e tests for modals and API client
331 lines
10 KiB
JavaScript
331 lines
10 KiB
JavaScript
import { describe, it, expect, beforeEach, vi, afterEach } from 'bun:test'
|
|
import { JSDOM } from 'jsdom'
|
|
|
|
// Set up DOM environment
|
|
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>')
|
|
global.document = dom.window.document
|
|
global.window = dom.window
|
|
global.bootstrap = {
|
|
Modal: vi.fn().mockImplementation(() => ({
|
|
show: vi.fn(),
|
|
hide: vi.fn()
|
|
}))
|
|
}
|
|
|
|
// Mock API and App
|
|
global.API = {
|
|
createTestimonial: vi.fn(),
|
|
updateTestimonial: vi.fn(),
|
|
createService: vi.fn(),
|
|
updateService: vi.fn(),
|
|
createFAQ: vi.fn(),
|
|
updateFAQ: vi.fn(),
|
|
createLead: vi.fn(),
|
|
updateLead: vi.fn()
|
|
}
|
|
|
|
global.app = {
|
|
showNotification: vi.fn(),
|
|
refreshSection: vi.fn()
|
|
}
|
|
|
|
describe('Admin Modals E2E Tests', () => {
|
|
beforeEach(() => {
|
|
// Clear all mocks before each test
|
|
vi.clearAllMocks()
|
|
|
|
// Clear DOM
|
|
document.body.innerHTML = ''
|
|
})
|
|
|
|
// Since the modal classes don't exist yet, we're writing tests that will fail (RED phase)
|
|
describe('TestimonialModal', () => {
|
|
let TestimonialModal
|
|
|
|
beforeEach(async () => {
|
|
try {
|
|
const module = await import('../../public/js/admin/modals/TestimonialModal.js')
|
|
TestimonialModal = module.TestimonialModal
|
|
} catch (e) {
|
|
// Expected since TestimonialModal doesn't exist yet
|
|
TestimonialModal = null
|
|
}
|
|
})
|
|
|
|
it('should open when "Añadir" button is clicked', () => {
|
|
// This test will fail because TestimonialModal doesn't exist yet
|
|
expect(TestimonialModal).not.toBeNull()
|
|
|
|
// Create section HTML with add button
|
|
document.body.innerHTML = `
|
|
<section id="section-testimonials">
|
|
<button id="addTestimonialBtn">Añadir testimonio</button>
|
|
<div id="testimonialsTableBody"></div>
|
|
</section>
|
|
`
|
|
|
|
// Create modal with mock onSave
|
|
const modal = new TestimonialModal(vi.fn())
|
|
|
|
// Simulate button click
|
|
const addButton = document.getElementById('addTestimonialBtn')
|
|
addButton.onclick = () => modal.open()
|
|
addButton.click()
|
|
|
|
// Verify modal opened
|
|
expect(modal.open).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should contain correct fields', () => {
|
|
// This test will fail because TestimonialModal doesn't exist yet
|
|
const modal = new TestimonialModal(vi.fn())
|
|
|
|
// Check that fields exist
|
|
const fields = modal.config.fields
|
|
expect(fields).toContainEqual(expect.objectContaining({ name: 'name', label: 'Nombre' }))
|
|
expect(fields).toContainEqual(expect.objectContaining({ name: 'location', label: 'Ubicación' }))
|
|
expect(fields).toContainEqual(expect.objectContaining({ name: 'rating', label: 'Valoración' }))
|
|
expect(fields).toContainEqual(expect.objectContaining({ name: 'text_es', label: 'Texto (ES)' }))
|
|
expect(fields).toContainEqual(expect.objectContaining({ name: 'text_ru', label: 'Texto (RU)' }))
|
|
expect(fields).toContainEqual(expect.objectContaining({ name: 'is_approved', label: 'Aprobado' }))
|
|
})
|
|
|
|
it('should call API on submit', async () => {
|
|
// This test will fail because TestimonialModal doesn't exist yet
|
|
const onSave = vi.fn()
|
|
const modal = new TestimonialModal(onSave)
|
|
|
|
// Mock form data
|
|
modal.collectData = vi.fn().mockReturnValue({
|
|
name: 'John Doe',
|
|
text_es: 'Great service',
|
|
is_approved: true
|
|
})
|
|
|
|
modal.validate = vi.fn().mockReturnValue({ valid: true, errors: [] })
|
|
|
|
// Mock API response
|
|
global.API.createTestimonial.mockResolvedValueOnce({ success: true })
|
|
|
|
// Trigger save
|
|
await modal._onSave()
|
|
|
|
// Verify API call
|
|
expect(global.API.createTestimonial).toHaveBeenCalledWith({
|
|
name: 'John Doe',
|
|
text_es: 'Great service',
|
|
is_approved: true
|
|
})
|
|
|
|
// Verify notification and section refresh
|
|
expect(global.app.showNotification).toHaveBeenCalledWith('Creado', 'success')
|
|
expect(global.app.refreshSection).toHaveBeenCalledWith('testimonials')
|
|
})
|
|
})
|
|
|
|
describe('ServiceModal', () => {
|
|
let ServiceModal
|
|
|
|
beforeEach(async () => {
|
|
try {
|
|
const module = await import('../../public/js/admin/modals/ServiceModal.js')
|
|
ServiceModal = module.ServiceModal
|
|
} catch (e) {
|
|
// Expected since ServiceModal doesn't exist yet
|
|
ServiceModal = null
|
|
}
|
|
})
|
|
|
|
it('should open when "Añadir" button is clicked', () => {
|
|
// This test will fail because ServiceModal doesn't exist yet
|
|
expect(ServiceModal).not.toBeNull()
|
|
|
|
// Create section HTML with add button
|
|
document.body.innerHTML = `
|
|
<section id="section-services">
|
|
<button id="addServiceBtn">Añadir servicio</button>
|
|
<div id="servicesTableBody"></div>
|
|
</section>
|
|
`
|
|
|
|
// Create modal with mock onSave
|
|
const modal = new ServiceModal(vi.fn())
|
|
|
|
// Simulate button click
|
|
const addButton = document.getElementById('addServiceBtn')
|
|
addButton.onclick = () => modal.open()
|
|
addButton.click()
|
|
|
|
// Verify modal opened
|
|
expect(modal.open).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should call API on submit', async () => {
|
|
// This test will fail because ServiceModal doesn't exist yet
|
|
const onSave = vi.fn()
|
|
const modal = new ServiceModal(onSave)
|
|
|
|
// Mock form data
|
|
modal.collectData = vi.fn().mockReturnValue({
|
|
title_es: 'New Service',
|
|
description_es: 'Service description'
|
|
})
|
|
|
|
modal.validate = vi.fn().mockReturnValue({ valid: true, errors: [] })
|
|
|
|
// Mock API response
|
|
global.API.createService.mockResolvedValueOnce({ success: true })
|
|
|
|
// Trigger save
|
|
await modal._onSave()
|
|
|
|
// Verify API call
|
|
expect(global.API.createService).toHaveBeenCalledWith({
|
|
title_es: 'New Service',
|
|
description_es: 'Service description'
|
|
})
|
|
|
|
// Verify notification and section refresh
|
|
expect(global.app.showNotification).toHaveBeenCalledWith('Creado', 'success')
|
|
expect(global.app.refreshSection).toHaveBeenCalledWith('services')
|
|
})
|
|
})
|
|
|
|
describe('FAQModal', () => {
|
|
let FAQModal
|
|
|
|
beforeEach(async () => {
|
|
try {
|
|
const module = await import('../../public/js/admin/modals/FAQModal.js')
|
|
FAQModal = module.FAQModal
|
|
} catch (e) {
|
|
// Expected since FAQModal doesn't exist yet
|
|
FAQModal = null
|
|
}
|
|
})
|
|
|
|
it('should open when "Añadir" button is clicked', () => {
|
|
// This test will fail because FAQModal doesn't exist yet
|
|
expect(FAQModal).not.toBeNull()
|
|
|
|
// Create section HTML with add button
|
|
document.body.innerHTML = `
|
|
<section id="section-faq">
|
|
<button id="addFAQBtn">Añadir FAQ</button>
|
|
<div id="faqTableBody"></div>
|
|
</section>
|
|
`
|
|
|
|
// Create modal with mock onSave
|
|
const modal = new FAQModal(vi.fn())
|
|
|
|
// Simulate button click
|
|
const addButton = document.getElementById('addFAQBtn')
|
|
addButton.onclick = () => modal.open()
|
|
addButton.click()
|
|
|
|
// Verify modal opened
|
|
expect(modal.open).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should call API on submit', async () => {
|
|
// This test will fail because FAQModal doesn't exist yet
|
|
const onSave = vi.fn()
|
|
const modal = new FAQModal(onSave)
|
|
|
|
// Mock form data
|
|
modal.collectData = vi.fn().mockReturnValue({
|
|
question_es: 'FAQ Question',
|
|
answer_es: 'FAQ Answer'
|
|
})
|
|
|
|
modal.validate = vi.fn().mockReturnValue({ valid: true, errors: [] })
|
|
|
|
// Mock API response
|
|
global.API.createFAQ.mockResolvedValueOnce({ success: true })
|
|
|
|
// Trigger save
|
|
await modal._onSave()
|
|
|
|
// Verify API call
|
|
expect(global.API.createFAQ).toHaveBeenCalledWith({
|
|
question_es: 'FAQ Question',
|
|
answer_es: 'FAQ Answer'
|
|
})
|
|
|
|
// Verify notification and section refresh
|
|
expect(global.app.showNotification).toHaveBeenCalledWith('Creado', 'success')
|
|
expect(global.app.refreshSection).toHaveBeenCalledWith('faq')
|
|
})
|
|
})
|
|
|
|
describe('LeadModal', () => {
|
|
let LeadModal
|
|
|
|
beforeEach(async () => {
|
|
try {
|
|
const module = await import('../../public/js/admin/modals/LeadModal.js')
|
|
LeadModal = module.LeadModal
|
|
} catch (e) {
|
|
// Expected since LeadModal doesn't exist yet
|
|
LeadModal = null
|
|
}
|
|
})
|
|
|
|
it('should open when "Añadir" button is clicked', () => {
|
|
// This test will fail because LeadModal doesn't exist yet
|
|
expect(LeadModal).not.toBeNull()
|
|
|
|
// Create section HTML with add button
|
|
document.body.innerHTML = `
|
|
<section id="section-leads">
|
|
<button id="addLeadBtn">Añadir lead</button>
|
|
<div id="leadsTableBody"></div>
|
|
</section>
|
|
`
|
|
|
|
// Create modal with mock onSave
|
|
const modal = new LeadModal(vi.fn())
|
|
|
|
// Simulate button click
|
|
const addButton = document.getElementById('addLeadBtn')
|
|
addButton.onclick = () => modal.open()
|
|
addButton.click()
|
|
|
|
// Verify modal opened
|
|
expect(modal.open).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should call API on submit', async () => {
|
|
// This test will fail because LeadModal doesn't exist yet
|
|
const onSave = vi.fn()
|
|
const modal = new LeadModal(onSave)
|
|
|
|
// Mock form data
|
|
modal.collectData = vi.fn().mockReturnValue({
|
|
name: 'John Doe',
|
|
email: 'john@example.com',
|
|
message: 'Hello'
|
|
})
|
|
|
|
modal.validate = vi.fn().mockReturnValue({ valid: true, errors: [] })
|
|
|
|
// Mock API response
|
|
global.API.createLead.mockResolvedValueOnce({ success: true })
|
|
|
|
// Trigger save
|
|
await modal._onSave()
|
|
|
|
// Verify API call
|
|
expect(global.API.createLead).toHaveBeenCalledWith({
|
|
name: 'John Doe',
|
|
email: 'john@example.com',
|
|
message: 'Hello'
|
|
})
|
|
|
|
// Verify notification and section refresh
|
|
expect(global.app.showNotification).toHaveBeenCalledWith('Creado', 'success')
|
|
expect(global.app.refreshSection).toHaveBeenCalledWith('leads')
|
|
})
|
|
})
|
|
}) |