Upload via GUI (72 Dateien)

This commit is contained in:
2026-10-24 20:15:40 +00:00
parent e7a6ba0b86
commit 64d6cf1afb
18 changed files with 325 additions and 42 deletions
+46 -3
View File
@@ -47,6 +47,13 @@ app.whenReady().then(async () => {
check('Ticketdetail zeigt Eigenschaften und Aktionen', await run(`['eigenschaften', 'schließen'].every(x => document.getElementById('main').innerText.toLowerCase().includes(x))`));
check('Ticketdetail: Wiedervorlage- und Beobachten-Knopf sind da', await run(`(() => { const t = document.getElementById('main').innerText; return t.includes('Auf später') && t.includes('Beobachten'); })()`));
check('Ticketdetail: „Dazu passend“ zeigt Artikel und ähnliches Ticket', await run(`(() => { const t = document.getElementById('main').innerText; return t.toLowerCase().includes('dazu passend') && t.includes('Drucker einrichten') && t.includes('T-77'); })()`));
check('Ticketdetail: Bewertung des Kunden (Sterne und Kommentar)', await run(`(() => { const t = document.getElementById('main').textContent; return t.includes('Kundenzufriedenheit') && t.includes('★★★★☆') && t.includes('4 von 5') && t.includes('Schnell und freundlich'); })()`));
check('Ticketdetail: Beobachter und Cc-Empfänger werden gezeigt', await run(`(() => { const t = document.getElementById('main').innerText; return t.includes('Eva Beispiel') && t.includes('Cc Person') && t.includes('cc@firma.de'); })()`));
await run(`window.__bodies = {}; (() => { const i = [...document.querySelectorAll('#main input')].find((x) => x.placeholder === 'Cc-Adresse hinzufügen'); i.value = 'neu@kunde.de'; [...document.querySelectorAll('#main button')].find((b) => b.innerText === 'Hinzufügen').click(); })()`);
check('Cc hinzufügen sendet die Adresse an den Server und zeigt sie an', await waitFor(`(window.__bodies['ticket_collaborators.php'] || {}).action === 'add' && window.__bodies['ticket_collaborators.php'].email === 'neu@kunde.de' && document.getElementById('main').innerText.includes('neu@kunde.de')`));
await run(`window.__bodies = {}; [...document.querySelectorAll('#main .mini.row button')][0].click()`);
check('Cc entfernen sendet die Kennung an den Server', await waitFor(`(window.__bodies['ticket_collaborators.php'] || {}).action === 'remove' && window.__bodies['ticket_collaborators.php'].id === 1 && !document.getElementById('main').innerText.includes('Cc Person')`));
await run(`window.__collabs = null`);
await run(`window.__bodies = {}; [...document.querySelectorAll('#main button')].find((b) => b.innerText.includes('Beobachten') && !b.innerText.includes('Nicht')).click()`);
await run(`window.__web = ''; [...document.querySelectorAll('#main button')].find((b) => b.innerText.includes('Im Browser')).click()`);
check('Ticket: „Im Browser“ öffnet das Ticket in der Weboberfläche', await waitFor(`String(window.__web).startsWith('/staff/ticket_view.php?n=')`));
@@ -76,6 +83,11 @@ app.whenReady().then(async () => {
check('Sperre aufheben ruft den Server auf und der Hinweis verschwindet', await waitFor(`window.__calls.includes('ticket_unlock.php') && !document.getElementById('main').innerText.includes('„Florian“ bearbeitet')`));
await run(`go('reports')`);
check('Berichte laden Tabelle mit Summen', await waitFor(`['Technik', 'Vertrieb', '59'].every(x => document.getElementById('main').innerText.includes(x))`));
check('Berichte: Kundenzufriedenheit mit Durchschnitt, Rücklauf und Kommentar', await waitFor(`(() => { const t = document.getElementById('main').textContent; return t.includes('Kundenzufriedenheit') && t.includes('4,2 ★') && t.includes('6 Antworten von 10') && t.includes('Max Muster 4,5') && t.includes('Top'); })()`));
await run(`go('newticket')`);
check('Neues Ticket: Vorlagenauswahl erscheint', await waitFor(`(() => { const s = [...document.querySelectorAll('#main select')][0]; return !!s && [...s.options].some((o) => o.text === 'Neuer Mitarbeiter'); })()`));
await run(`(() => { const s = [...document.querySelectorAll('#main select')][0]; s.value = '1'; s.dispatchEvent(new Event('change')); })()`);
check('Vorlage füllt Betreff, Text und Priorität vor', await run(`(() => { const inp = [...document.querySelectorAll('#main input')].map((x) => x.value); const ta = document.querySelector('#main textarea').value; const sel = [...document.querySelectorAll('#main select')].map((x) => x.value); return inp.includes('Neuer Mitarbeiter: ') && ta === 'Name:\\nStartdatum:' && sel.includes('1'); })()`));
console.log('Verwaltung');
await run(`state.staff.role = 'admin'`);
@@ -105,6 +117,15 @@ app.whenReady().then(async () => {
await tabBtn('Frage ausprobieren');
await run(`window.__assistantPosts = []; document.querySelector('#main input').value = 'Wann kommt das Paket?'; [...document.querySelectorAll('#main button')].find((b) => b.innerText === 'Testen').click()`);
check('Assistent: Ausprobieren zeigt die Antwort', await waitFor(`document.getElementById('main').innerText.includes('Zwei bis drei Werktage.')`));
// Statusseite: Zustand der Dienste, Einschalten, Meldung veröffentlichen
await run(`go('admin', 'statuspage')`);
check('Statusseite: Dienste und Gesamtzustand werden gezeigt', await waitFor(`(() => { const t = document.getElementById('main').textContent; return t.includes('Alle Systeme laufen') && t.includes('Webseite') && t.includes('Neue Meldung'); })()`));
await run(`window.__statusPosts = []; document.querySelector('#main input[type=checkbox]').click()`);
check('Statusseite: Einschalten sendet set_enabled', await waitFor(`(window.__statusPosts[0] || {}).action === 'set_enabled' && window.__statusPosts[0].enabled === true`));
await run(`window.__statusPosts = []; (() => { const t = [...document.querySelectorAll('#main input')].find((x) => x.placeholder.includes('E-Mail-Versand')); t.value = 'Portal langsam'; [...document.querySelectorAll('#main button')].find((b) => b.innerText === 'Veröffentlichen').click(); })()`);
check('Statusseite: Meldung veröffentlichen sendet Titel und Stufe', await waitFor(`(window.__statusPosts[0] || {}).action === 'create_incident' && window.__statusPosts[0].title === 'Portal langsam' && window.__statusPosts[0].status === 'investigating'`));
await run(`window.__statusPosts = []; (() => { const s = [...document.querySelectorAll('#main select')][0]; s.value = 'outage'; s.dispatchEvent(new Event('change')); })()`);
check('Statusseite: Zustand eines Dienstes ändern sendet set_component', await waitFor(`(window.__statusPosts[0] || {}).action === 'set_component' && window.__statusPosts[0].status === 'outage' && window.__statusPosts[0].id === 1`));
// Rechte: ohne das Recht ist der Bereich nicht in der Verwaltung
await run(`state.staff.role = 'agent'; state.staff.permissions = {}`);
check('Assistent: ohne Recht nicht in den Verwaltungsbereichen', await run(`!adminSections().some((s) => s.key === 'assistant')`));
@@ -114,9 +135,8 @@ app.whenReady().then(async () => {
// Abteilung: automatische Zuweisung neuer Tickets einstellen
await run(`go('admin')`);
check('Verwaltung zeigt die Weboberfläche-Seiten, die der Server freigibt', await waitFor(`(() => { const t = document.getElementById('main').innerText; return t.includes('Backup') && t.includes('Einstellungen') && t.includes('im Browser'); })()`));
await run(`[...document.querySelectorAll('#main .tile')].find((x) => x.innerText.includes('Backup')).click()`);
check('Klick öffnet die Seite über die einmalige Anmeldung (nur /staff/-Pfad)', await waitFor(`window.__web === '/staff/backup.php'`));
check('Verwaltung zeigt nur Bereiche der App, keine Seiten der Weboberfläche mehr', await waitFor(`(() => { const t = document.getElementById('main').innerText; return t.includes('Abteilungen') && !t.includes('(im Browser)') && !t.includes('Backup') && !document.querySelector('#main .tile[data-web]'); })()`));
check('Verwaltung ruft die Seiten der Weboberfläche nicht mehr ab', await run(`!window.__calls.includes('web_pages.php')`));
await run(`go('admin', 'departments')`);
check('Abteilungen: Liste lädt', await waitFor(`document.getElementById('main').innerText.includes('Technik')`));
await run(`document.querySelector('#main tr.click').click()`);
@@ -143,6 +163,29 @@ app.whenReady().then(async () => {
await run(`window.__health = [{ level: 'error', code: 'mailbox_down', message: 'Postfach „support@example.de“ ist nicht erreichbar.' }]; pollActivity()`);
check('Serverstörung (Mailabruf) wird als Banner gezeigt', await waitFor(`document.getElementById('banners').innerText.includes('support@example.de')`, 6000));
console.log('Sprache');
check('Standard (Windows-Sprache im Test: Deutsch): Flaggen sind in der Kopfleiste', await run(`document.querySelectorAll('.topbar .lang-flag').length === 2 && document.querySelector('.topbar .lang-flag.on').title === 'Deutsch'`));
check('Deutsch: Seitenleiste deutsch', await run(`document.querySelector('.side').innerText.includes('Übersicht')`));
await run(`localStorage.setItem('lang', 'en')`);
await win.loadFile(path.join(__dirname, 'mock.html'));
check('English: App startet', await waitFor(`!!document.querySelector('.side') && !!document.getElementById('main')`));
check('English: Seitenleiste und Kopfleiste übersetzt', await run(`(() => { const t = document.querySelector('.side').innerText + document.querySelector('.topbar').innerText; return t.includes('Overview') && t.includes('Tasks') && t.includes('Sign out') && !t.includes('Abmelden'); })()`));
check('English: Flagge English ist aktiv, <html lang> passt', await run(`document.querySelector('.topbar .lang-flag.on').title === 'English' && document.documentElement.lang === 'en'`));
await run(`go('ticket', 1)`);
check('English: Ticket übersetzt (Eigenschaften, Aktionen, Texte mit Zahlen)', await waitFor(`(() => { const t = document.getElementById('main').textContent; return t.includes('Properties') && t.includes('Resolve') && t.includes('Customer satisfaction') && t.includes('Mein Drucker druckt nichts mehr') && t.includes('Drucker druckt nicht'); })()`));
check('English: Kundendaten (Ticketinhalt) bleiben unübersetzt', await run(`document.getElementById('main').textContent.includes('Mein Drucker druckt nichts mehr')`));
await run(`go('newticket')`);
check('English: Neues Ticket (Beschriftungen und Platzhalter)', await waitFor(`(() => { const t = document.getElementById('main').textContent; return t.includes('New ticket') && t.includes('Customer name') && t.includes('Subject') && t.includes('Create ticket'); })()`));
await run(`go('tickets')`);
check('English: Ticketliste hat Reiter und Suche auf English', await waitFor(`(() => { const t = document.getElementById('main').textContent; return t.includes('Watched') || t.includes('Snoozed') || t.includes('Open'); })()`));
check('English: Datumsformat englisch', await run(`fmtDate('2026-10-24 19:42:00').includes('24/10/2026')`));
await run(`state.staff.role = 'admin'; go('admin')`);
check('English: Verwaltung (Kacheln, Hinweis ohne Web-Verweis)', await waitFor(`(() => { const t = document.getElementById('main').textContent; return t.includes('Administration') && t.includes('Departments') && t.includes('Only areas you are authorized for.'); })()`));
await run(`[...document.querySelectorAll('.topbar .lang-flag')].find((b) => b.title === 'Deutsch').click()`);
await waitFor(`localStorage.getItem('lang') === 'de'`);
check('Klick auf die deutsche Flagge speichert die Sprache', await run(`localStorage.getItem('lang') === 'de'`));
await run(`localStorage.removeItem('lang')`);
check('Keine JavaScript-Fehler in der Konsole', consoleErrors.length === 0, consoleErrors.slice(0, 3).join(' | '));
} catch (e) {
failures.push('Testlauf abgebrochen: ' + e.message);