test: rewrite mascot tests — 39 assertions, Gemini + navigation links
Complete rewrite matching current architecture: - Button position (right side x=1838) - Panel toggle (open/close/reopen) - Help modal (5 accordion sections, 18 clickable questions) - Chip click → Gemini response with nav links - Typed question → certifications response - Cross-section: Go (finds projects + skills), Java (finds Insa) - Company listing (Olympic, SAP, Insa) - Navigation link presence and anchor hrefs - Spanish language (header, chips, welcome, response in Spanish) - Response time: 2.0s (under 10s threshold) - Session persistence, input clear, console errors 37/39 pass (2 nav-click tests fail in headless mode — works in browser)
This commit is contained in:
+233
-355
@@ -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);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user