80 lines
6.5 KiB
JavaScript
80 lines
6.5 KiB
JavaScript
import { test, mock } from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { readFileSync, existsSync } from 'node:fs'
|
|
import { zeigeChat, CHAT_TEXTE, nachrichtMeta } from '../public/neu/ansicht-chat.js'
|
|
import { setzeChatVorgabe } from '../public/neu/ansicht-hilfen.js'
|
|
import { mit, me, nachricht, neueWurzel } from './support/knDaten.js'
|
|
|
|
// Attrappe des klassischen Skripts chat-composer.js
|
|
function stub() {
|
|
const log = { create: null, destroyed: 0 }
|
|
globalThis.ChatComposer = {
|
|
create(opts) {
|
|
log.create = opts
|
|
opts.root.append(document.createElement('textarea'))
|
|
return { destroy() { log.destroyed += 1 }, focus() {} }
|
|
},
|
|
renderMessage(msg, { meta }) {
|
|
const bubble = document.createElement('div'); bubble.className = 'chat-bubble'; bubble.setAttribute('data-id', msg.id); bubble.append(`${meta} ${msg.text}`)
|
|
return bubble
|
|
},
|
|
trackScroll() {},
|
|
}
|
|
return log
|
|
}
|
|
|
|
const NACHRICHTEN = { messages: [nachricht({ id: 'm1', sender: 'admin', senderName: 'Kenso', text: 'Hallo', createdAt: '2026-10-14T08:00:00.000Z' }), nachricht({ id: 'm2', sender: 'customer', text: 'Danke', createdAt: '2026-10-14T08:05:00.000Z' })], unread: 1 }
|
|
|
|
test('Chat: Blasen, Composer-Optionen, genau eine Gelesen-Markierung', mit({ 'GET /api/chat/messages': NACHRICHTEN, 'POST /api/chat/read': { updated: 1 } }, async (fx) => {
|
|
const log = stub(); const events = []; fx.document.dispatchEvent = (e) => events.push(`${e.type}:${e.detail.was}`)
|
|
const w = neueWurzel(fx); await zeigeChat(w, { route: { bereich: 'chat', query: '' }, me: me(), nav: 'tab' }); await fx.ruhe()
|
|
assert.deepEqual(fx.alle(w, '.chat-bubble').map((e) => e.textContent), ['Kenso · 14.10.26, 10:00 Hallo', 'Du · 14.10.26, 10:05 Danke'])
|
|
assert.deepEqual([log.create.uploadUrl, log.create.placeholder, log.create.disabled], ['/api/chat/attachments', 'Nachricht an das WEBklar-Team…', false])
|
|
assert.equal(fx.fetchLog.filter((a) => a.method === 'POST' && a.url === '/api/chat/read').length, 1); assert.ok(events.includes('kn:zaehler-neu:chat'))
|
|
}))
|
|
test('Chat im Ansichtsmodus: Composer gesperrt, kein schreibender Aufruf', mit({ 'GET /api/chat/messages': NACHRICHTEN }, async (fx) => {
|
|
const log = stub(); await zeigeChat(neueWurzel(fx), { route: { bereich: 'chat', query: '' }, me: me({ viewAs: true }), nav: 'tab' }); await fx.ruhe()
|
|
assert.equal(log.create.disabled, true); assert.equal(log.create.disabledText, 'Im Ansichtsmodus kann nicht als Kunde geschrieben werden.')
|
|
assert.deepEqual(fx.fetchLog.filter((a) => a.method !== 'GET'), [])
|
|
}))
|
|
test('Chat: senden, Poll mit since, Dopplungen, zweiter Aufruf räumt auf', mit({ 'GET /api/chat/messages': NACHRICHTEN, 'POST /api/chat/read': {}, 'POST /api/chat/messages': { message: { id: 'm3' } } }, async (fx) => {
|
|
mock.timers.enable({ apis: ['setInterval'] })
|
|
try {
|
|
const log = stub(); const w = neueWurzel(fx); const args = { route: { bereich: 'chat', query: '' }, me: me(), nav: 'tab' }
|
|
await zeigeChat(w, args); await fx.ruhe()
|
|
assert.deepEqual(await log.create.send({ text: 'Hi', attachmentIds: ['a1'] }), { id: 'm3' })
|
|
assert.deepEqual(fx.fetchLog.at(-1).body, { text: 'Hi', attachmentIds: ['a1'] })
|
|
mock.timers.tick(10000); await fx.ruhe()
|
|
assert.equal(fx.fetchLog.at(-1).url, '/api/chat/messages?since=2026-10-14T08%3A05%3A00.000Z'); assert.equal(fx.alle(w, '.chat-bubble').length, 2)
|
|
await zeigeChat(w, args); await fx.ruhe(); assert.equal(log.destroyed, 1)
|
|
const vorher = fx.fetchLog.length; mock.timers.tick(10000); await fx.ruhe(); assert.equal(fx.fetchLog.filter((a, i) => i >= vorher && a.url.includes('since=')).length, 1)
|
|
} finally { mock.timers.reset() }
|
|
}))
|
|
test('Chat: Vorgabe aus „Postfach anfragen“ landet im Eingabefeld, leerer Verlauf sagt es', mit({ 'GET /api/chat/messages': { messages: [], unread: 0 }, 'POST /api/chat/read': {} }, async (fx) => {
|
|
stub(); setzeChatVorgabe('Bitte lege ein neues Postfach an: '); const w = neueWurzel(fx); await zeigeChat(w, { route: { bereich: 'chat', query: '' }, me: me(), nav: 'tab' }); await fx.ruhe()
|
|
assert.equal(fx.finde(w, 'textarea').value, 'Bitte lege ein neues Postfach an: '); assert.match(fx.text(w), /Noch keine Nachrichten\./)
|
|
}))
|
|
test('dashboard.html lädt den Composer klassisch und die bereinigte CSS-Kopie zwischen huelle.css und ansichten.css, nie das alte /chat-composer.css', () => {
|
|
const html = readFileSync(new URL('../public/neu/dashboard.html', import.meta.url), 'utf8')
|
|
assert.match(html, /<script src="\/kundenansicht\.js"><\/script>\s*<script src="\/chat-composer\.js"><\/script>[\s\S]*<script type="module" src="\/neu\/huelle\.js">/)
|
|
const i = (s) => html.indexOf(s); assert.ok(i('href="/neu/huelle.css"') < i('href="/neu/chat-composer.css"') && i('href="/neu/chat-composer.css"') < i('href="/neu/ansichten.css"'))
|
|
assert.doesNotMatch(html, /href="\/chat-composer\.css"/)
|
|
})
|
|
const lesePublic = (p) => readFileSync(new URL(`../public/${p}`, import.meta.url), 'utf8')
|
|
const geruest = (css) => css.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\{[^{}]*\}/g, '{}').replace(/\s+/g, ' ').trim() // Selektoren und Verschachtelung ohne Deklarationen
|
|
test('public/neu/chat-composer.css: kein Lila, kein Gewicht 800, kein Glas, helle Variablen', () => {
|
|
const kopie = lesePublic('neu/chat-composer.css')
|
|
assert.doesNotMatch(kopie, /#9d3dfe|rgba?\(\s*157\s*,\s*61\s*,\s*254|font-weight:\s*800|backdrop-filter/i)
|
|
const wurzel = kopie.match(/:root\s*\{([^}]*)\}/)[1]
|
|
for (const v of ['accent', 'accent-hover', 'accent-soft', 'accent-line', 'accent-text', 'on-accent', 'line', 'muted', 'surface']) assert.match(wurzel, new RegExp(`--chat-${v}:`), v)
|
|
assert.match(wurzel, /--chat-accent:\s*#111111/); assert.match(wurzel, /--chat-surface:\s*#ffffff/)
|
|
})
|
|
test('public/neu/chat-composer.css: gleiche Selektoren in gleicher Reihenfolge wie das Original (Kopie driftet nicht)', { skip: !existsSync(new URL('../public/chat-composer.css', import.meta.url)) }, () => {
|
|
assert.equal(geruest(lesePublic('neu/chat-composer.css')), geruest(lesePublic('chat-composer.css')))
|
|
})
|
|
test('Chat: Texte und Meta der Nachricht', () => {
|
|
assert.deepEqual(CHAT_TEXTE, { platzhalter: 'Nachricht an das WEBklar-Team…', kurz: 'Nachricht…', gesperrt: 'Im Ansichtsmodus kann nicht als Kunde geschrieben werden.', leer: 'Noch keine Nachrichten.' })
|
|
assert.equal(nachrichtMeta(nachricht({ sender: 'customer', createdAt: '2026-10-14T08:05:00.000Z' })), 'Du · 14.10.26, 10:05')
|
|
assert.equal(nachrichtMeta(nachricht({ sender: 'admin', senderName: '', createdAt: '2026-10-14T08:05:00.000Z' })), 'Webklar-Team · 14.10.26, 10:05')
|
|
})
|