diff --git a/tests/mjs/83-chat-mascot.test.mjs b/tests/mjs/83-chat-mascot.test.mjs index 4b3cb18..b7e5ac9 100644 --- a/tests/mjs/83-chat-mascot.test.mjs +++ b/tests/mjs/83-chat-mascot.test.mjs @@ -2,483 +2,361 @@ /** * CV ASSISTANT MASCOT TEST * ========================= - * Tests the AI chat widget (mascot) functionality - * - Widget visibility and toggle - * - Help popup display and dismiss - * - Suggested question chips - * - Text input and form submission - * - Chat messages rendering - * - Session persistence - * - Language awareness - * - Responsive behavior + * Tests the AI chat mascot: UI, chips, navigation links, help modal, + * Gemini responses, cross-section intelligence, and bilingual support. */ import { chromium } from 'playwright'; const URL = "http://localhost:1999"; -const CHAT_TIMEOUT = 15000; // Agent responses can take a few seconds +const CHAT_TIMEOUT = 20000; async function testChatMascot() { console.log('🤖 CV ASSISTANT MASCOT TEST\n'); console.log('='.repeat(70)); - const browser = await chromium.launch({ headless: false }); + const browser = await chromium.launch({ headless: true }); const page = await browser.newPage({ viewport: { width: 1920, height: 1080 } }); const errors = []; - const testResults = []; let passed = 0; let failed = 0; + const results = []; - function record(name, success, detail = '') { - testResults.push({ name, success, detail }); - if (success) { - passed++; - console.log(` ✅ ${name}${detail ? ' — ' + detail : ''}`); - } else { - failed++; - console.log(` ❌ ${name}${detail ? ' — ' + detail : ''}`); - } + function record(name, ok, detail = '') { + results.push({ name, ok }); + ok ? passed++ : failed++; + console.log(` ${ok ? '✅' : '❌'} ${name}${detail ? ' — ' + detail : ''}`); } page.on('console', msg => { - if (msg.type() === 'error') { - errors.push(msg.text()); - } + if (msg.type() === 'error') errors.push(msg.text()); }); - // ======================================================================== - // SETUP: Load page - // ======================================================================== + // ====================================================================== console.log("\n📋 Loading page..."); await page.goto(`${URL}/?lang=en`); await page.waitForTimeout(2000); - // ======================================================================== - // TEST 1: Mascot button exists and is visible - // ======================================================================== - console.log("\n1️⃣ Mascot Button Presence"); + // ====================================================================== + // 1. BUTTON + // ====================================================================== + console.log("\n1️⃣ Mascot Button"); - const btnExists = await page.locator('#chat-toggle-btn').isVisible(); - record('Mascot toggle button is visible', btnExists); + record('Button visible', await page.locator('#chat-toggle-btn').isVisible()); + record('Robot icon visible', await page.locator('.chat-icon-open').isVisible()); + record('Close icon hidden', await page.locator('.chat-icon-close').isHidden()); - const btnIcon = await page.locator('#chat-toggle-btn .chat-icon-open').isVisible(); - record('Mascot icon (robot) is visible', btnIcon); + const btnBox = await page.locator('#chat-toggle-btn').boundingBox(); + record('Button on right side', btnBox && btnBox.x > 1800, `x=${btnBox?.x}`); - const closeIconHidden = await page.locator('#chat-toggle-btn .chat-icon-close').isHidden(); - record('Close icon is hidden initially', closeIconHidden); + // ====================================================================== + // 2. PANEL TOGGLE + // ====================================================================== + console.log("\n2️⃣ Panel Toggle"); - // ======================================================================== - // TEST 2: Chat panel starts closed - // ======================================================================== - console.log("\n2️⃣ Initial State"); - - const panelHidden = await page.locator('#chat-panel').isHidden(); - record('Chat panel is hidden initially', panelHidden); - - // ======================================================================== - // TEST 3: Click mascot opens chat panel - // ======================================================================== - console.log("\n3️⃣ Open Chat Panel"); + record('Panel hidden initially', await page.locator('#chat-panel').isHidden()); await page.click('#chat-toggle-btn'); await page.waitForTimeout(500); + record('Panel opens on click', await page.locator('#chat-panel.chat-open').isVisible()); + record('Button has mascot-active', (await page.locator('#chat-toggle-btn.mascot-active').count()) > 0); - const panelOpen = await page.locator('#chat-panel').isVisible(); - record('Chat panel opens on click', panelOpen); + await page.click('#chat-toggle-btn'); + await page.waitForTimeout(300); + record('Panel closes on second click', await page.locator('#chat-panel').isHidden()); - const hasMascotActive = await page.locator('#chat-toggle-btn.mascot-active').count(); - record('Button has mascot-active class', hasMascotActive > 0); - - // ======================================================================== - // TEST 4: Help card is visible on first open - // ======================================================================== - console.log("\n4️⃣ Help Card (Onboarding)"); - - const helpVisible = await page.locator('#chat-help-card.visible').isVisible(); - record('Help card is visible on first open', helpVisible); - - const helpText = await page.locator('.chat-help-text').textContent(); - record('Help text contains assistant description', - helpText.includes('CV Assistant') || helpText.includes('Asistente')); - - const dismissBtn = await page.locator('.chat-help-dismiss').isVisible(); - record('Dismiss button is visible', dismissBtn); - - // ======================================================================== - // TEST 5: Dismiss help card - // ======================================================================== - console.log("\n5️⃣ Dismiss Help Card"); - - await page.click('.chat-help-dismiss'); + await page.click('#chat-toggle-btn'); await page.waitForTimeout(300); - const helpDismissed = await page.locator('#chat-help-card.visible').count(); - record('Help card dismissed after click', helpDismissed === 0); + // ====================================================================== + // 3. HELP MODAL + // ====================================================================== + console.log("\n3️⃣ Help Modal"); - // ======================================================================== - // TEST 6: Help button re-toggles help card - // ======================================================================== - console.log("\n6️⃣ Re-toggle Help Card"); + record('Help button (?) visible', await page.locator('.chat-help-btn').isVisible()); await page.click('.chat-help-btn'); + await page.waitForTimeout(500); + const modalOpen = await page.locator('#chat-help-modal').evaluate(el => el.open); + record('Help modal opens', modalOpen); + + const accordionCount = await page.locator('.chat-help-group').count(); + record('5 accordion sections', accordionCount === 5, `found ${accordionCount}`); + + const firstOpen = await page.locator('.chat-help-group[open]').count(); + record('First section expanded by default', firstOpen >= 1); + + const questionCount = await page.locator('.chat-help-q').count(); + record('18+ clickable questions', questionCount >= 18, `found ${questionCount}`); + + // Close modal + await page.locator('#chat-help-modal .info-modal-close').click(); await page.waitForTimeout(300); - const helpReOpened = await page.locator('#chat-help-card.visible').isVisible(); - record('Help card re-opens via ? button', helpReOpened); + // ====================================================================== + // 4. WELCOME MESSAGE + // ====================================================================== + console.log("\n4️⃣ Welcome Message"); - // Dismiss again for clean state - await page.click('.chat-help-dismiss'); - await page.waitForTimeout(200); + const welcome = await page.locator('#chat-messages .chat-agent').first().textContent(); + record('Welcome message present', welcome.includes('Ask me anything') || welcome.includes('Pregúntame')); - // ======================================================================== - // TEST 7: Initial welcome message exists - // ======================================================================== - console.log("\n7️⃣ Welcome Message"); - - const welcomeMsg = await page.locator('#chat-messages .chat-agent').first().textContent(); - record('Welcome message is present', - welcomeMsg.includes('Ask me anything') || welcomeMsg.includes('Pregúntame')); - - // ======================================================================== - // TEST 8: Suggested question chips exist - // ======================================================================== - console.log("\n8️⃣ Suggested Question Chips"); + // ====================================================================== + // 5. CHIPS + // ====================================================================== + console.log("\n5️⃣ Suggested Chips"); const chipCount = await page.locator('.chat-chip').count(); - record('5 suggested question chips exist', chipCount === 5, `found ${chipCount}`); + record('5 chips exist', chipCount === 5, `found ${chipCount}`); - const firstChipText = await page.locator('.chat-chip').first().textContent(); - record('First chip has text', firstChipText.length > 0, firstChipText); + // ====================================================================== + // 6. CHIP CLICK → GEMINI RESPONSE + // ====================================================================== + console.log("\n6️⃣ Chip Click → Response"); - // ======================================================================== - // TEST 9: Text input exists and is functional - // ======================================================================== - console.log("\n9️⃣ Text Input"); + const msgsBefore = await page.locator('#chat-messages .chat-message').count(); + await page.locator('.chat-chip').first().click(); - const inputExists = await page.locator('#chat-input').isVisible(); - record('Chat input is visible', inputExists); - - const placeholder = await page.locator('#chat-input').getAttribute('placeholder'); - record('Input has placeholder', - placeholder.includes('Ask something') || placeholder.includes('Pregunta')); - - // ======================================================================== - // TEST 10: Send button exists - // ======================================================================== - console.log("\n🔟 Send Button"); - - const sendBtn = await page.locator('.chat-send-btn').isVisible(); - record('Send button is visible', sendBtn); - - // ======================================================================== - // TEST 11: Chip click fills input and submits - // ======================================================================== - console.log("\n1️⃣1️⃣ Chip Click → Submit"); - - const msgCountBefore = await page.locator('#chat-messages .chat-message').count(); - - await page.click('.chat-chip >> text=Go projects'); - // Wait for any new message (user or error — agent may be unavailable) + // Wait for response await page.waitForFunction( - (before) => document.querySelectorAll('#chat-messages .chat-message').length > before, - msgCountBefore, + (before) => document.querySelectorAll('#chat-messages .chat-message').length > before + 1, + msgsBefore, { timeout: CHAT_TIMEOUT } ); const userMsg = await page.locator('#chat-messages .chat-user').last().textContent(); - record('User message appears after chip click', - userMsg.includes('Go projects'), userMsg.substring(0, 50)); - - // Wait for agent response - await page.waitForSelector('#chat-messages .chat-agent:nth-child(3)', { timeout: CHAT_TIMEOUT }); + record('User message appears', userMsg.length > 5, userMsg.substring(0, 40)); const agentMsg = await page.locator('#chat-messages .chat-agent').last().textContent(); - record('Agent response appears', - agentMsg.length > 20, `${agentMsg.substring(0, 60)}...`); + record('Agent response appears', agentMsg.length > 30, `${agentMsg.substring(0, 50)}...`); - record('Agent mentions Go projects', - agentMsg.toLowerCase().includes('go') || agentMsg.toLowerCase().includes('immich') || agentMsg.toLowerCase().includes('cmux')); + // ====================================================================== + // 7. NAVIGATION LINKS IN RESPONSE + // ====================================================================== + console.log("\n7️⃣ Navigation Links"); - // ======================================================================== - // TEST 12: Type and submit custom question - // ======================================================================== - console.log("\n1️⃣2️⃣ Type Custom Question"); + const navLinkCount = await page.locator('#chat-messages .chat-nav-link').count(); + record('Response has navigation links', navLinkCount > 0, `found ${navLinkCount}`); - const userMsgCountBefore = await page.locator('#chat-messages .chat-user').count(); - await page.fill('#chat-input', 'How many years of experience?'); + if (navLinkCount > 0) { + const firstLinkHref = await page.locator('#chat-messages .chat-nav-link').first().getAttribute('href'); + record('Links have anchor hrefs', firstLinkHref && firstLinkHref.startsWith('#'), firstLinkHref); + } + + // ====================================================================== + // 8. TYPED QUESTION + // ====================================================================== + console.log("\n8️⃣ Typed Question"); + + const msgsBeforeType = await page.locator('#chat-messages .chat-user').count(); + await page.fill('#chat-input', 'What certifications does he have?'); await page.click('.chat-send-btn'); - // Wait for a new user message to appear await page.waitForFunction( (count) => document.querySelectorAll('#chat-messages .chat-user').length > count, - userMsgCountBefore, + msgsBeforeType, { timeout: CHAT_TIMEOUT } ); - const customUserMsg = await page.locator('#chat-messages .chat-user').last().textContent(); - record('Custom typed message appears', - customUserMsg.includes('years of experience')); - // Wait for agent response - await page.waitForFunction(() => { - const msgs = document.querySelectorAll('#chat-messages .chat-agent'); - return msgs.length >= 3; - }, { timeout: CHAT_TIMEOUT }); + const typedMsg = await page.locator('#chat-messages .chat-user').last().textContent(); + record('Typed message appears', typedMsg.includes('certifications')); - const customAgentMsg = await page.locator('#chat-messages .chat-agent').last().textContent(); - record('Agent responds to custom question', - customAgentMsg.length > 10, `${customAgentMsg.substring(0, 60)}...`); + const certResponse = await page.locator('#chat-messages .chat-agent').last().textContent(); + record('Certifications response', certResponse.toLowerCase().includes('sap') || certResponse.toLowerCase().includes('certif')); - // ======================================================================== - // TEST 13: Input clears after submit - // ======================================================================== - console.log("\n1️⃣3️⃣ Input Clear After Submit"); + // ====================================================================== + // 9. INPUT CLEARS + // ====================================================================== + console.log("\n9️⃣ Input Clear"); - const inputValueAfter = await page.locator('#chat-input').inputValue(); - record('Input is cleared after submission', inputValueAfter === ''); + const inputVal = await page.locator('#chat-input').inputValue(); + record('Input cleared after submit', inputVal === ''); - // ======================================================================== - // TEST 14: Session ID persisted - // ======================================================================== - console.log("\n1️⃣4️⃣ Session Persistence"); + // ====================================================================== + // 10. SESSION PERSISTENCE + // ====================================================================== + console.log("\n🔟 Session"); const sessionId = await page.locator('#chat-session-id').inputValue(); - record('Session ID is set after response', - sessionId.length > 0, sessionId.substring(0, 20) + '...'); + record('Session ID set', sessionId.length > 10, sessionId.substring(0, 20)); - // ======================================================================== - // TEST 15: Close and reopen panel - // ======================================================================== - console.log("\n1️⃣5️⃣ Close and Reopen"); + // ====================================================================== + // 11. CROSS-SECTION INTELLIGENCE (Go) + // ====================================================================== + console.log("\n1️⃣1️⃣ Intelligence: Go cross-section"); - await page.click('#chat-toggle-btn'); - await page.waitForTimeout(300); + await page.fill('#chat-input', 'What is Juan\'s experience with Go?'); + await page.click('.chat-send-btn'); - const panelClosedAgain = await page.locator('#chat-panel').isHidden(); - record('Panel closes on second toggle click', panelClosedAgain); + const agentsBefore11 = await page.locator('#chat-messages .chat-agent').count(); + await page.waitForFunction( + (c) => document.querySelectorAll('#chat-messages .chat-agent').length > c, + agentsBefore11, + { timeout: CHAT_TIMEOUT } + ); - await page.click('#chat-toggle-btn'); - await page.waitForTimeout(300); + const goResp = (await page.locator('#chat-messages .chat-agent').last().textContent()).toLowerCase(); + record('Go: finds projects', goResp.includes('immich') || goResp.includes('cmux')); + record('Go: finds skills', goResp.includes('skill') || goResp.includes('proficiency') || goResp.includes('programming')); - const panelReopened = await page.locator('#chat-panel').isVisible(); - record('Panel reopens on third click', panelReopened); + // ====================================================================== + // 12. CROSS-SECTION INTELLIGENCE (Java) + // ====================================================================== + console.log("\n1️⃣2️⃣ Intelligence: Java cross-section"); - // Messages should still be there - const msgCountAfterReopen = await page.locator('#chat-messages .chat-message').count(); - record('Messages preserved after reopen', msgCountAfterReopen >= 3, - `${msgCountAfterReopen} messages`); + await page.fill('#chat-input', 'What Java experience does he have?'); + await page.click('.chat-send-btn'); - // ======================================================================== - // TEST 16: Chat header content - // ======================================================================== - console.log("\n1️⃣6️⃣ Header Content"); + const agentsBefore12 = await page.locator('#chat-messages .chat-agent').count(); + await page.waitForFunction( + (c) => document.querySelectorAll('#chat-messages .chat-agent').length > c, + agentsBefore12, + { timeout: CHAT_TIMEOUT } + ); - const headerText = await page.locator('.chat-header span').textContent(); - record('Header shows "CV Assistant"', - headerText.includes('CV Assistant') || headerText.includes('Asistente')); + const javaResp = (await page.locator('#chat-messages .chat-agent').last().textContent()).toLowerCase(); + record('Java: finds Insa', javaResp.includes('insa')); + record('Java: finds multiple companies', javaResp.includes('homeria') || javaResp.includes('webratio') || javaResp.includes('penta')); - const helpBtnExists = await page.locator('.chat-help-btn').isVisible(); - record('Help button (?) in header', helpBtnExists); + // ====================================================================== + // 13. COMPANIES LIST + // ====================================================================== + console.log("\n1️⃣3️⃣ Intelligence: Companies"); - // ======================================================================== - // TEST 17: Spanish language version - // ======================================================================== - console.log("\n1️⃣7️⃣ Spanish Language"); + await page.fill('#chat-input', 'List all companies he worked at'); + await page.click('.chat-send-btn'); - // Close chat first - await page.click('#chat-toggle-btn'); + const agentsBefore13 = await page.locator('#chat-messages .chat-agent').count(); + await page.waitForFunction( + (c) => document.querySelectorAll('#chat-messages .chat-agent').length > c, + agentsBefore13, + { timeout: CHAT_TIMEOUT } + ); + + const compResp = (await page.locator('#chat-messages .chat-agent').last().textContent()).toLowerCase(); + record('Lists Olympic', compResp.includes('olympic')); + record('Lists SAP', compResp.includes('sap')); + record('Lists Insa', compResp.includes('insa')); + + // ====================================================================== + // 14. NAVIGATION LINK CLICK (scroll + highlight) + // ====================================================================== + console.log("\n1️⃣4️⃣ Navigation Link Click"); + + const navLinks = page.locator('#chat-messages .chat-nav-link'); + const navCount = await navLinks.count(); + if (navCount > 0) { + await navLinks.first().click(); + await page.waitForTimeout(1000); + // Panel should close after nav click + const panelAfterNav = await page.locator('#chat-panel.chat-open').count(); + record('Panel closes after nav click', panelAfterNav === 0); + + // Check highlight exists somewhere + const highlighted = await page.locator('.chat-highlight').count(); + record('Target element highlighted', highlighted > 0); + + // Reopen chat for remaining tests + await page.click('#chat-toggle-btn'); + await page.waitForTimeout(300); + } else { + record('Navigation link click (no links)', false, 'no nav links found'); + record('Target highlight (skipped)', false); + } + + // ====================================================================== + // 15. SPANISH LANGUAGE + // ====================================================================== + console.log("\n1️⃣5️⃣ Spanish Language"); + + await page.click('#chat-toggle-btn'); // close await page.waitForTimeout(200); - await page.goto(`${URL}/?lang=es`); await page.waitForTimeout(2000); - await page.click('#chat-toggle-btn'); await page.waitForTimeout(500); - const esHeaderText = await page.locator('.chat-header span').textContent(); - record('Spanish header shows "Asistente del CV"', - esHeaderText.includes('Asistente del CV')); + const esHeader = await page.locator('.chat-header span').textContent(); + record('Spanish header', esHeader.includes('Asistente')); - const esChipText = await page.locator('.chat-chip').first().textContent(); - record('Spanish chips are in Spanish', - esChipText.includes('Go') || esChipText.includes('Proyectos')); + const esChip = await page.locator('.chat-chip').first().textContent(); + record('Spanish chips', esChip.includes('Go') || esChip.includes('Proyectos')); const esWelcome = await page.locator('#chat-messages .chat-agent').first().textContent(); - record('Spanish welcome message', - esWelcome.includes('Pregúntame') || esWelcome.includes('Hola')); + record('Spanish welcome', esWelcome.includes('Pregúntame')); - const esPlaceholder = await page.locator('#chat-input').getAttribute('placeholder'); - record('Spanish placeholder', - esPlaceholder.includes('Pregunta')); + // ====================================================================== + // 16. SPANISH RESPONSE + // ====================================================================== + console.log("\n1️⃣6️⃣ Spanish Intelligence"); - // ======================================================================== - // TEST 18: Empty message handling - // ======================================================================== - console.log("\n1️⃣8️⃣ Empty Message Handling"); - - const msgCountBeforeEmpty = await page.locator('#chat-messages .chat-message').count(); - await page.fill('#chat-input', ''); + await page.fill('#chat-input', '¿Cuántos años de experiencia tiene?'); await page.click('.chat-send-btn'); - await page.waitForTimeout(1000); - const msgCountAfterEmpty = await page.locator('#chat-messages .chat-message').count(); - // Should show error or stay the same - record('Empty message handled gracefully', msgCountAfterEmpty >= msgCountBeforeEmpty); + const agentsBefore16 = await page.locator('#chat-messages .chat-agent').count(); + await page.waitForFunction( + (c) => document.querySelectorAll('#chat-messages .chat-agent').length > c, + agentsBefore16, + { timeout: CHAT_TIMEOUT } + ); - // ======================================================================== - // TEST 19: No console errors - // ======================================================================== - console.log("\n1️⃣9️⃣ Console Errors"); + const esResp = await page.locator('#chat-messages .chat-agent').last().textContent(); + record('Responds in Spanish', esResp.includes('años') || esResp.includes('experiencia')); + record('Reports 21 years', esResp.includes('21')); - const chatErrors = errors.filter(e => - e.includes('chat') || e.includes('htmx') || e.includes('hyperscript')); - record('No chat-related console errors', chatErrors.length === 0, - chatErrors.length > 0 ? chatErrors.join(', ') : 'clean'); + // ====================================================================== + // 17. RESPONSE TIME + // ====================================================================== + console.log("\n1️⃣7️⃣ Response Time"); - // ======================================================================== - // TEST 20: Chat panel CSS positioning - // ======================================================================== - console.log("\n2️⃣0️⃣ CSS Positioning"); - - const btnPos = await page.locator('#chat-toggle-btn').boundingBox(); - const viewportWidth = 1920; - record('Button is on the right side of screen', - btnPos && btnPos.x > viewportWidth / 2, `x=${btnPos?.x}`); - - const panelPos = await page.locator('#chat-panel').boundingBox(); - record('Panel is on the right side', - panelPos && (panelPos.x + panelPos.width) > viewportWidth / 2, `right edge=${panelPos ? panelPos.x + panelPos.width : 0}`); - - // ======================================================================== - // TEST 21-25: INTELLIGENCE TESTS (verify agent gives complete answers) - // ======================================================================== - console.log("\n2️⃣1️⃣ Intelligence: Go projects (cross-section)"); - - // Go back to English for intelligence tests + // Go back to English for timing test + await page.click('#chat-toggle-btn'); await page.goto(`${URL}/?lang=en`); await page.waitForTimeout(2000); await page.click('#chat-toggle-btn'); - await page.waitForTimeout(500); - // Dismiss help card if visible - const helpStillVisible = await page.locator('#chat-help-card.visible').count(); - if (helpStillVisible > 0) { - await page.click('.chat-help-dismiss'); - await page.waitForTimeout(200); - } - - // Ask about Go — should find projects AND experience AND skills - await page.fill('#chat-input', 'What is Juan\'s experience with Go?'); - await page.click('.chat-send-btn'); - await page.waitForFunction( - () => { - const agents = document.querySelectorAll('#chat-messages .chat-agent'); - return agents.length >= 2 && agents[agents.length - 1].textContent.length > 50; - }, - { timeout: CHAT_TIMEOUT } - ); - - const goResponse = await page.locator('#chat-messages .chat-agent').last().textContent(); - const goLower = goResponse.toLowerCase(); - record('Go search finds projects', - goLower.includes('immich') || goLower.includes('cmux'), - goLower.includes('immich') ? 'found Immich' : 'missing Immich'); - record('Go search finds skills section', - goLower.includes('skill') || goLower.includes('programming') || goLower.includes('proficiency'), - 'mentions skills'); - - console.log("\n2️⃣2️⃣ Intelligence: Company count"); - - await page.fill('#chat-input', 'How many companies has Juan worked at? List them all.'); - await page.click('.chat-send-btn'); - await page.waitForFunction( - () => { - const agents = document.querySelectorAll('#chat-messages .chat-agent'); - return agents.length >= 3 && agents[agents.length - 1].textContent.length > 100; - }, - { timeout: CHAT_TIMEOUT } - ); - - const companiesResponse = await page.locator('#chat-messages .chat-agent').last().textContent(); - const compLower = companiesResponse.toLowerCase(); - record('Lists Olympic Broadcasting', compLower.includes('olympic')); - record('Lists Insa', compLower.includes('insa')); - record('Lists Gigya or SAP', compLower.includes('gigya') || compLower.includes('sap')); - - console.log("\n2️⃣3️⃣ Intelligence: Years of experience"); + await page.waitForTimeout(300); + const startTime = Date.now(); await page.fill('#chat-input', 'How many years of experience?'); await page.click('.chat-send-btn'); + + const agentsBefore17 = await page.locator('#chat-messages .chat-agent').count(); await page.waitForFunction( - () => { - const agents = document.querySelectorAll('#chat-messages .chat-agent'); - return agents.length >= 4 && agents[agents.length - 1].textContent.length > 20; - }, + (c) => document.querySelectorAll('#chat-messages .chat-agent').length > c, + agentsBefore17, { timeout: CHAT_TIMEOUT } ); + const responseTime = Date.now() - startTime; + record('Response under 10 seconds', responseTime < 10000, `${(responseTime / 1000).toFixed(1)}s`); - const yearsResponse = await page.locator('#chat-messages .chat-agent').last().textContent(); - record('Reports 21 years of experience', - yearsResponse.includes('21'), `response: ${yearsResponse.substring(0, 80)}`); + // ====================================================================== + // 18. ERROR-FREE + // ====================================================================== + console.log("\n1️⃣8️⃣ Console Errors"); - console.log("\n2️⃣4️⃣ Intelligence: React cross-section"); + const chatErrors = errors.filter(e => e.includes('chat') || e.includes('htmx')); + record('No chat console errors', chatErrors.length === 0, + chatErrors.length > 0 ? chatErrors.join('; ') : 'clean'); - await page.fill('#chat-input', 'Has Juan worked with React? Where?'); - await page.click('.chat-send-btn'); - await page.waitForFunction( - () => { - const agents = document.querySelectorAll('#chat-messages .chat-agent'); - return agents.length >= 5 && agents[agents.length - 1].textContent.length > 50; - }, - { timeout: CHAT_TIMEOUT } - ); - - const reactResponse = await page.locator('#chat-messages .chat-agent').last().textContent(); - const reactLower = reactResponse.toLowerCase(); - record('React search finds experience entries', - reactLower.includes('olympic') || reactLower.includes('liv') || reactLower.includes('gigya')); - - console.log("\n2️⃣5️⃣ Intelligence: Spanish response language"); - - await page.fill('#chat-input', '¿Cuántas certificaciones tiene Juan?'); - await page.click('.chat-send-btn'); - await page.waitForFunction( - () => { - const agents = document.querySelectorAll('#chat-messages .chat-agent'); - return agents.length >= 6 && agents[agents.length - 1].textContent.length > 20; - }, - { timeout: CHAT_TIMEOUT } - ); - - const esResponse = await page.locator('#chat-messages .chat-agent').last().textContent(); - record('Responds in Spanish when asked in Spanish', - esResponse.includes('certificaci') || esResponse.includes('SAP') || esResponse.includes('tiene')); - - // ======================================================================== + // ====================================================================== // SUMMARY - // ======================================================================== + // ====================================================================== console.log('\n' + '='.repeat(70)); - console.log(`\n📊 RESULTS: ${passed} passed, ${failed} failed (${testResults.length} total)\n`); + console.log(`\n📊 RESULTS: ${passed} passed, ${failed} failed (${results.length} total)\n`); if (failed > 0) { - console.log('❌ FAILED TESTS:'); - testResults.filter(t => !t.success).forEach(t => { - console.log(` • ${t.name}${t.detail ? ' — ' + t.detail : ''}`); - }); + console.log('❌ FAILED:'); + results.filter(r => !r.ok).forEach(r => console.log(` • ${r.name}`)); console.log(''); } - if (errors.length > 0) { - console.log(`⚠️ Console errors: ${errors.length}`); - errors.forEach(e => console.log(` • ${e}`)); - } - await browser.close(); - console.log(failed === 0 ? '✅ ALL TESTS PASSED!' : '❌ SOME TESTS FAILED'); process.exit(failed > 0 ? 1 : 0); } testChatMascot().catch(err => { - console.error('💥 Test crashed:', err.message); + console.error('💥 Crash:', err.message); process.exit(1); });