const { chromium } = require('playwright'); const path = require('path'); const fs = require('fs'); const BASE = 'https://it-nexus.cereda-systems.de'; const USER = 'superadmin'; const PASS = 'Admin123!'; const OUT = path.join(__dirname, 'screenshots'); if (!fs.existsSync(OUT)) fs.mkdirSync(OUT, { recursive: true }); const issues = []; const checks = []; function note(section, msg) { issues.push(`[${section}] ${msg}`); console.log(` ⚠️ ${msg}`); } function ok(section, msg) { checks.push(`[${section}] ✓ ${msg}`); console.log(` ✅ ${msg}`); } function info(msg) { console.log(` ℹ️ ${msg}`); } async function shot(page, name) { const file = path.join(OUT, `${name}.png`); await page.screenshot({ path: file, fullPage: true }); console.log(` 📷 ${name}.png gespeichert`); } async function waitNet(page) { await page.waitForLoadState('networkidle').catch(() => {}); await page.waitForTimeout(1500); } (async () => { const browser = await chromium.launch({ headless: true }); const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 } }); const page = await ctx.newPage(); // 404-Fehler mit URL aufzeichnen const notFound404 = []; const consoleErrors = []; page.on('response', r => { if (r.status() === 404) notFound404.push(r.url()); }); page.on('console', m => { if (m.type() === 'error') consoleErrors.push(m.text()); }); // ── 1. LOGIN ─────────────────────────────────────────────────────────────── console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log('🔐 LOGIN'); await page.goto(`${BASE}/login`); await waitNet(page); await shot(page, '01-login'); const userInput = page.locator('input[type="text"], input[name="username"], input[placeholder*="user" i], input[placeholder*="name" i]').first(); const passInput = page.locator('input[type="password"]').first(); await userInput.fill(USER); await passInput.fill(PASS); await shot(page, '02-login-filled'); await passInput.press('Enter'); await waitNet(page); await shot(page, '03-after-login'); if (page.url().includes('login')) { note('Login', `Login fehlgeschlagen — URL: ${page.url()}`); const errText = await page.locator('.error, [class*="error"], [class*="alert"]').first().innerText().catch(() => ''); if (errText) note('Login', `Fehlermeldung: ${errText}`); await browser.close(); printSummary(); return; } ok('Login', `Eingeloggt als ${USER} → ${page.url()}`); // ── 2. DASHBOARD ────────────────────────────────────────────────────────── console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log('📊 DASHBOARD'); await page.goto(`${BASE}/dashboard`); await waitNet(page); await shot(page, '04-dashboard'); const bodyText = await page.locator('body').innerText(); if (bodyText.includes('Dashboard') || bodyText.includes('Willkommen')) ok('Dashboard', 'Seite geladen'); else note('Dashboard', 'Dashboard-Inhalt nicht erkennbar'); // ── 3. MONITORING ───────────────────────────────────────────────────────── console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log('🖥️ MONITORING'); await page.goto(`${BASE}/monitoring`); await waitNet(page); await shot(page, '05-monitoring'); // Geräte const agentRows = await page.locator('tr[data-id], tbody tr, [class*="agent-row"]').count(); info(`${agentRows} Tabellenzeilen sichtbar`); // Warte bis Monitoring-Tabelle geladen ist await page.waitForTimeout(4000); await shot(page, '05b-monitoring-loaded'); // IT-NB-02 per API prüfen — mit Auth-Token aus localStorage const apiData = await page.evaluate(async () => { try { const token = localStorage.getItem('token') || localStorage.getItem('authToken') || Object.keys(localStorage).map(k=>localStorage.getItem(k)).find(v=>v?.startsWith?.('eyJ')); const headers = token ? { Authorization: `Bearer ${token}` } : {}; const r = await fetch('/api/monitoring', { headers }); if (!r.ok) return { error: r.status }; const j = await r.json(); return j.data?.find(a => a.hostname === 'IT-NB-02') || null; } catch(e) { return { error: e.message }; } }); if (apiData && !apiData.error) { ok('Monitoring', `IT-NB-02 via API gefunden (v${apiData.agent_version})`); info(`OS: ${apiData.os_name || '?'}`); info(`BitLocker: ${apiData.bitlocker_status || '?'}`); info(`Defender: enabled=${apiData.defender_enabled}, age=${apiData.defender_signatures_age}`); info(`Serial: ${apiData.hardware_serial || '?'}`); if ((apiData.os_name || '').includes('11')) ok('Monitoring-API', `OS = ${apiData.os_name} ✓`); else note('Monitoring-API', `OS = ${apiData.os_name || 'null'} — erwartet Windows 11`); if (apiData.bitlocker_status === 'encrypted') ok('Monitoring-API', 'BitLocker = encrypted ✓'); else note('Monitoring-API', `BitLocker = ${apiData.bitlocker_status || 'null'}`); if (apiData.defender_enabled) ok('Monitoring-API', `Defender aktiv ✓ (Signaturen ${apiData.defender_signatures_age}d alt)`); else note('Monitoring-API', 'Defender nicht aktiv oder Daten fehlen'); if (apiData.hardware_serial) ok('Monitoring-API', `Seriennummer = ${apiData.hardware_serial} ✓`); else note('Monitoring-API', 'Seriennummer fehlt'); } else { note('Monitoring-API', `Fehler: ${apiData?.error || 'IT-NB-02 nicht gefunden'}`); } // Detail-Modal über Lupen-Button öffnen const itnb02Text = page.locator('text=IT-NB-02').first(); if (await itnb02Text.isVisible().catch(() => false)) { ok('Monitoring', 'IT-NB-02 in Tabelle sichtbar'); await itnb02Text.scrollIntoViewIfNeeded(); await page.waitForTimeout(500); // Selektoren in Reihenfolge versuchen bis einer funktioniert let clicked = false; const selectors = [ 'tr:has-text("IT-NB-02") button:first-child', 'tr:has-text("IT-NB-02") button', '[class*="row"]:has-text("IT-NB-02") button', 'button:near(:text("IT-NB-02"))', ]; for (const sel of selectors) { try { const btn = page.locator(sel).first(); if (await btn.isVisible({ timeout: 1000 }).catch(() => false)) { await btn.click(); clicked = true; info(`Modal via Selektor geöffnet: ${sel}`); break; } } catch { } } if (!clicked) { // Letzter Fallback: direkt auf IT-NB-02 Text klicken await itnb02Text.click(); info('Modal via Text-Klick versucht'); } await page.waitForTimeout(2500); await shot(page, '06-monitoring-detail-IT-NB-02'); const detail = await page.locator('body').innerText(); // Prüfe ob Modal offen (suche nach Sicherheits-Sektion-Keywords) const hasModal = detail.includes('Sicherheit') || detail.includes('BitLocker') || detail.includes('Seriennummer'); info(`Modal offen: ${hasModal} | Body-Länge: ${detail.length} Zeichen`); if (hasModal) { if (detail.includes('Verschlüsselt')) ok('Monitoring-Modal', 'BitLocker = Verschlüsselt ✓'); else note('Monitoring-Modal', 'BitLocker kein "Verschlüsselt" text'); if (detail.includes('Defender') && detail.includes('Aktiv')) ok('Monitoring-Modal', 'Defender = Aktiv ✓'); else note('Monitoring-Modal', 'Defender Status unklar'); if (detail.includes('5CD44318HR')) ok('Monitoring-Modal', 'Seriennummer 5CD44318HR ✓'); else note('Monitoring-Modal', 'Seriennummer fehlt'); if (detail.includes('Arctic Wolf') || detail.includes('Docker') || detail.includes('IT Nexus Agent')) ok('Monitoring-Modal', 'Software-Liste befüllt ✓'); else note('Monitoring-Modal', 'Software fehlt'); } else { note('Monitoring-Modal', 'Detail-Modal hat nicht geöffnet — Sicherheits-Sektion nicht sichtbar'); } await page.keyboard.press('Escape'); await page.waitForTimeout(500); } else { note('Monitoring', 'IT-NB-02 nicht gefunden'); } // Warnungen prüfen (BitLocker off, Defender inaktiv) const monBody = await page.locator('body').innerText(); if (monBody.includes('BitLocker aus')) note('Monitoring', 'Andere Geräte: BitLocker nicht aktiv — Warnung sichtbar'); if (monBody.includes('Defender inaktiv')) note('Monitoring', 'Andere Geräte: Defender inaktiv — Warnung sichtbar'); // ── 4. PATCH MANAGEMENT ─────────────────────────────────────────────────── console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log('🔄 PATCH MANAGEMENT'); await page.goto(`${BASE}/patch-management`); await waitNet(page); await shot(page, '07-patch-management'); const patchBody = await page.locator('body').innerText(); // Version if (patchBody.includes('v2.0.0')) ok('Patch', 'v2.0.0 als aktuelle Version angezeigt ✓'); else note('Patch', 'v2.0.0 nicht sichtbar'); if (patchBody.includes('1/1 aktualisiert')) ok('Patch', 'Test-Gruppe: 1/1 aktualisiert ✓'); else note('Patch', 'Test-Gruppe nicht korrekt aktualisiert'); // IT-NB-02 OS im Patch-Management const itnb02PatchRow = page.locator('tr').filter({ hasText: 'IT-NB-02' }).first(); if (await itnb02PatchRow.isVisible().catch(() => false)) { const rowText = await itnb02PatchRow.innerText(); info(`IT-NB-02 Patch-Row: ${rowText.replace(/\s+/g, ' ').substring(0, 120)}`); if (rowText.includes('Win 11') || rowText.includes('11 Pro')) ok('Patch', 'IT-NB-02 OS = Win 11 ✓'); else if (rowText.includes('Win 10')) note('Patch', 'IT-NB-02 noch Win 10 — Check-in ausstehend'); if (rowText.includes('🪟')) ok('Patch', 'Win11-Badge = 🪟 Win 11 ✓'); else if (rowText.includes('Fähig')) note('Patch', 'Win11-Badge zeigt noch "Fähig" statt "🪟 Win 11"'); } // Gruppen-Tab const gruppenTab = page.locator('text=Gruppen').first(); if (await gruppenTab.isVisible()) { await gruppenTab.click(); await page.waitForTimeout(1000); await shot(page, '08-patch-gruppen'); } // ── 5. HELPDESK / TICKETS ───────────────────────────────────────────────── console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log('🎫 HELPDESK'); await page.goto(`${BASE}/helpdesk`).catch(() => page.goto(`${BASE}/tickets`)); await waitNet(page); await shot(page, '09-helpdesk'); const hdBody = await page.locator('body').innerText(); if (hdBody.includes('Ticket') || hdBody.includes('Helpdesk')) ok('Helpdesk', 'Seite geladen ✓'); else note('Helpdesk', 'Inhalt nicht erkennbar'); // ── 6. KI-ASSISTENT ─────────────────────────────────────────────────────── console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log('🤖 KI-ASSISTENT'); await page.goto(`${BASE}/ai`).catch(() => page.goto(`${BASE}/ai-assistant`)).catch(() => {}); await waitNet(page); await shot(page, '10-ki'); const kiBody = await page.locator('body').innerText(); if (kiBody.includes('KI') || kiBody.includes('Assistent') || kiBody.includes('Claude')) ok('KI', 'KI-Assistent Seite geladen ✓'); else note('KI', 'KI-Seite nicht gefunden oder leer'); // ── 7. 404 / CONSOLE ERRORS ─────────────────────────────────────────────── if (notFound404.length > 0) { console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log('🔴 404-FEHLER:'); [...new Set(notFound404)].forEach(u => note('404', u.replace(BASE, ''))); } if (consoleErrors.length > 0) { console.log('\n🔴 KONSOLEN-FEHLER:'); [...new Set(consoleErrors)].slice(0, 5).forEach(e => info(`Console: ${e.substring(0, 100)}`)); } await browser.close(); printSummary(); })(); function printSummary() { console.log('\n' + '═'.repeat(60)); console.log(`📊 ZUSAMMENFASSUNG: ${checks.length} OK, ${issues.length} Probleme`); if (issues.length > 0) { console.log('\n⚠️ ZU FIXEN:'); issues.forEach((i, n) => console.log(` ${n+1}. ${i}`)); } else { console.log('✅ Alles in Ordnung!'); } console.log('\n📁 Screenshots: playwright-tests/screenshots/'); console.log('═'.repeat(60) + '\n'); }