Files
Webklar-Kundenbereich/test/knAuftragsZeitleiste.test.js
ClaudeandClaude Sonnet 5.5 14dc2ad1bf Kundenansicht neu (Aufträge): Aufträge mit Rückfrage vor den übrigen laufenden, nie hinter "Ältere Aufträge anzeigen"
sortiereAuftraege stellt unter den laufenden Aufträgen die mit Rückfrage (braucheInfo) nach vorn, danach
wie bisher die jüngste Aktivität, dann die Reihenfolge des Servers. Erledigte und Stornierte bleiben dahinter.
Tests: Reihenfolge mit Rückfragen und Gruppierung (die Rückfrage mit der ältesten Aktivität bleibt sichtbar).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-17 15:47:21 +00:00

166 lines
11 KiB
JavaScript

// „Was wir zuletzt erledigt haben“ als Zeitleiste (Variante C, Pilot-Feedback 17.10.): reine Logik (Fortschritt, Reihenfolge, Gruppierung, Kürzen).
// Antwortform von GET /api/activity laut server/routes/customerData.js und services/customerProjectView.js. Der DOM-Bau steht in knAuftragsZeitleisteDom.test.js.
import { test } from 'node:test'
import assert from 'node:assert/strict'
import {
MAX_AUFTRAEGE, MAX_ZEILEN, STUFEN, fortschritt, braucheInfo, letzteAktivitaet, sortiereAuftraege, gruppiereAuftraege, kuerze,
eintragsKurztext, datumKurz, zeitleistenZeilen,
} from '../public/neu/auftrags-zeitleiste.js'
import { AUFTRAG_SCHRITTE } from '../public/neu/website-details.js'
const JETZT = new Date('2026-10-16T09:00:00Z')
const ST = {
neu: { label: 'Eingegangen', tone: 'new', step: 1 },
arbeit: { label: 'In Arbeit', tone: 'active', step: 2 },
wartet: { label: 'Wartet auf deine Rückmeldung', tone: 'waiting', step: 2 },
pruefung: { label: 'Wird geprüft', tone: 'review', step: 3 },
fertig: { label: 'Erledigt', tone: 'done', step: 4 },
pause: { label: 'Pausiert', tone: 'paused', step: 0 },
storniert: { label: 'Storniert', tone: 'stopped', step: 0 },
}
const auftrag = (nr, st, createdAt, entries = [], topic = `Thema ${nr}`) => ({ number: String(nr), topic, status: ST[st], createdAt, entries })
const eintrag = (date, text, over = {}) => ({ kind: 'online', label: 'Online erledigt', date, minutes: 15, person: 'Lena Vogel', text, ...over })
const nummern = (liste) => liste.map((a) => a.number)
/* ------------------------------------------------------------------ reine Logik --- */
test('Stufen: dieselben vier Namen wie der Fortschritt der Projektdetails', () => {
assert.deepEqual(STUFEN, ['Eingegangen', 'In Arbeit', 'Prüfung', 'Erledigt'])
assert.deepEqual(STUFEN, AUFTRAG_SCHRITTE)
})
test('Fortschritt: erreichte Abschnitte bis zum aktuellen, Stufe 0 ist leer', () => {
const stand = (step) => fortschritt({ step }).map((s) => [s.erreicht, s.aktuell])
assert.deepEqual(stand(1), [[true, true], [false, false], [false, false], [false, false]])
assert.deepEqual(stand(2), [[true, false], [true, true], [false, false], [false, false]])
assert.deepEqual(stand(3), [[true, false], [true, false], [true, true], [false, false]])
assert.deepEqual(stand(4), [[true, false], [true, false], [true, false], [true, true]])
assert.deepEqual(fortschritt({ step: 4 }).map((s) => s.name), STUFEN)
for (const leer of [0, undefined, null, 'x', -1, 9]) assert.deepEqual(fortschritt({ step: leer }).map((s) => s.erreicht), [false, false, false, false], `step ${String(leer)}`)
assert.deepEqual(fortschritt(null).map((s) => s.erreicht), [false, false, false, false])
})
test('Info vom Kunden nötig: genau der Ton „waiting“ (Status „awaiting“ des Servers)', () => {
assert.equal(braucheInfo(auftrag(1, 'wartet', '2026-10-01T00:00:00Z')), true)
for (const st of ['neu', 'arbeit', 'pruefung', 'fertig', 'pause', 'storniert']) assert.equal(braucheInfo(auftrag(1, st, '2026-10-01T00:00:00Z')), false, st)
assert.equal(braucheInfo({}), false); assert.equal(braucheInfo(null), false)
})
test('Letzte Aktivität: der jüngste Eintrag oder die Anlage des Auftrags, je nachdem, was später ist', () => {
const a = auftrag(1, 'arbeit', '2026-10-01T10:00:00Z', [eintrag('2026-10-05', 'x'), eintrag('2026-10-09', 'y'), eintrag('', 'ohne Datum')])
assert.equal(letzteAktivitaet(a), Date.parse('2026-10-09T00:00:00Z'))
assert.equal(letzteAktivitaet(auftrag(2, 'arbeit', '2026-10-12T10:00:00Z', [eintrag('2026-10-05', 'x')])), Date.parse('2026-10-12T10:00:00Z'))
assert.equal(letzteAktivitaet(auftrag(3, 'arbeit', '', [])), 0)
assert.equal(letzteAktivitaet({ entries: 'kaputt', createdAt: 'nie' }), 0)
})
test('Reihenfolge: laufende zuerst (letzte Aktivität), dann erledigte (neueste zuerst), Storniertes ganz hinten', () => {
const liste = [
auftrag(1, 'fertig', '2026-09-01T00:00:00Z', [eintrag('2026-10-14', 'fertig, aber jüngster Eintrag')]),
auftrag(2, 'arbeit', '2026-10-01T00:00:00Z', [eintrag('2026-10-03', 'alt')]),
auftrag(3, 'storniert', '2026-10-15T00:00:00Z'),
auftrag(4, 'wartet', '2026-10-02T00:00:00Z', [eintrag('2026-10-13', 'neuer')]),
auftrag(5, 'fertig', '2026-09-20T00:00:00Z', [eintrag('2026-09-25', 'älter')]),
auftrag(6, 'pause', '2026-10-04T00:00:00Z'),
auftrag(7, 'pruefung', '2026-10-05T00:00:00Z'),
auftrag(8, 'neu', '2026-10-12T00:00:00Z'),
]
assert.deepEqual(nummern(sortiereAuftraege(liste)), ['4', '8', '7', '6', '2', '1', '5', '3'])
assert.deepEqual(nummern(liste), ['1', '2', '3', '4', '5', '6', '7', '8'], 'die Eingabe bleibt unverändert')
})
test('Reihenfolge: Aufträge mit Rückfrage stehen vor den übrigen laufenden (auch wenn sie am ältesten sind), unter sich nach Aktivität; erledigte bleiben dahinter', () => {
const liste = [
auftrag(1, 'arbeit', '2026-10-14T00:00:00Z'),
auftrag(2, 'wartet', '2026-09-01T00:00:00Z', [eintrag('2026-09-05', 'alt')]),
auftrag(3, 'neu', '2026-10-13T00:00:00Z'),
auftrag(4, 'fertig', '2026-10-15T00:00:00Z'),
auftrag(5, 'wartet', '2026-09-20T00:00:00Z', [eintrag('2026-10-02', 'etwas neuer')]),
auftrag(6, 'pause', '2026-10-12T00:00:00Z'),
auftrag(7, 'pruefung', '2026-10-11T00:00:00Z'),
auftrag(8, 'storniert', '2026-10-16T00:00:00Z'),
auftrag(9, 'arbeit', '2026-10-10T00:00:00Z'),
]
assert.deepEqual(nummern(sortiereAuftraege(liste)), ['5', '2', '1', '3', '6', '7', '9', '4', '8'])
// ohne Rückfrage ändert sich nichts an der alten Regel (Aktivität, dann Serverreihenfolge)
assert.deepEqual(nummern(sortiereAuftraege(liste.filter((a) => !braucheInfo(a)))), ['1', '3', '6', '7', '9', '4', '8'])
})
test('Gruppierung: eine Rückfrage verschwindet nie hinter „Ältere Aufträge anzeigen“, auch wenn sie die älteste Aktivität hat', () => {
const laufend = Array.from({ length: 6 }, (_, i) => auftrag(200 + i, 'arbeit', `2026-10-0${i + 3}T00:00:00Z`))
const rueckfrage = auftrag(300, 'wartet', '2026-08-01T00:00:00Z', [eintrag('2026-08-02', 'sehr alt')])
const zweite = auftrag(301, 'wartet', '2026-07-01T00:00:00Z')
const g = gruppiereAuftraege([...laufend, rueckfrage, zweite])
assert.deepEqual(nummern(g.sichtbar.slice(0, 2)), ['300', '301'])
assert.equal(g.sichtbar.length, 5)
assert.ok(nummern(g.sichtbar).includes('300') && nummern(g.sichtbar).includes('301'))
assert.ok(g.aeltere.every((a) => !braucheInfo(a)))
// mehr Rückfragen als Plätze: die mit der jüngsten Aktivität stehen vorn, der Rest gehört in „älter“ (kein Auftrag geht verloren)
const viele = Array.from({ length: 7 }, (_, i) => auftrag(400 + i, 'wartet', `2026-10-0${i + 1}T00:00:00Z`))
const g2 = gruppiereAuftraege(viele)
assert.deepEqual(nummern(g2.sichtbar), ['406', '405', '404', '403', '402'])
assert.deepEqual(nummern(g2.aeltere), ['401', '400'])
})
test('Reihenfolge: bei gleicher Aktivität bleibt die Reihenfolge des Servers', () => {
const liste = ['9', '8', '7'].map((n) => auftrag(n, 'arbeit', '2026-10-10T00:00:00Z'))
assert.deepEqual(nummern(sortiereAuftraege(liste)), ['9', '8', '7'])
})
test('Gruppierung: höchstens fünf Aufträge sichtbar, der Rest ist „älter“', () => {
assert.equal(MAX_AUFTRAEGE, 5)
const liste = Array.from({ length: 7 }, (_, i) => auftrag(100 + i, 'arbeit', `2026-10-0${i + 1}T00:00:00Z`))
const g = gruppiereAuftraege(liste)
assert.deepEqual(nummern(g.sichtbar), ['106', '105', '104', '103', '102']); assert.deepEqual(nummern(g.aeltere), ['101', '100'])
const fuenf = gruppiereAuftraege(liste.slice(0, 5)); assert.equal(fuenf.sichtbar.length, 5); assert.deepEqual(fuenf.aeltere, [])
assert.deepEqual(gruppiereAuftraege([]), { sichtbar: [], aeltere: [] }); assert.deepEqual(gruppiereAuftraege(undefined), { sichtbar: [], aeltere: [] })
assert.equal(gruppiereAuftraege(liste, 2).aeltere.length, 5)
})
test('Gruppierung: ein erledigter Auftrag rutscht hinter laufende, auch wenn er neuer ist', () => {
const liste = [auftrag(1, 'fertig', '2026-10-15T00:00:00Z'), ...Array.from({ length: 5 }, (_, i) => auftrag(10 + i, 'arbeit', `2026-09-0${i + 1}T00:00:00Z`))]
const g = gruppiereAuftraege(liste)
assert.deepEqual(nummern(g.aeltere), ['1'])
})
test('Kürzen: die ersten n sichtbar, der Rest getrennt; Eingabe bleibt', () => {
assert.equal(MAX_ZEILEN, 3)
const a = [1, 2, 3, 4, 5]
assert.deepEqual(kuerze(a, 3), { sichtbar: [1, 2, 3], rest: [4, 5] })
assert.deepEqual(kuerze([1, 2], 3), { sichtbar: [1, 2], rest: [] })
assert.deepEqual(kuerze(undefined, 3), { sichtbar: [], rest: [] })
assert.deepEqual(a, [1, 2, 3, 4, 5])
})
test('Datum: TT.MM., mit Jahr nur wenn es nicht das laufende ist', () => {
assert.equal(datumKurz('2026-10-02', JETZT), '02.10.')
assert.equal(datumKurz('2025-12-24', JETZT), '24.12.25')
assert.equal(datumKurz('2026-10-02T08:00:00.000Z', JETZT), '02.10.')
for (const kaputt of ['', null, undefined, 'gestern', '2026-13']) assert.equal(datumKurz(kaputt, JETZT), '', String(kaputt))
})
test('Kurztext: Änderungen, freier Text oder Art der Arbeit; nie lang', () => {
assert.equal(eintragsKurztext({ kind: 'update', label: 'Website aktualisiert', changes: ['Menü angepasst'] }), 'Menü angepasst')
assert.equal(eintragsKurztext({ kind: 'update', label: 'Website aktualisiert', changes: ['Menü angepasst', 'Bilder getauscht'] }), 'Menü angepasst und 1 weitere Änderung')
assert.equal(eintragsKurztext({ kind: 'update', label: 'Website aktualisiert', changes: ['A', 'B', 'C', 'D'] }), 'A und 3 weitere Änderungen')
assert.equal(eintragsKurztext({ kind: 'online', label: 'Online erledigt', text: 'Neue Öffnungszeiten eingetragen.' }), 'Neue Öffnungszeiten eingetragen.')
assert.equal(eintragsKurztext({ kind: 'onsite', label: 'Vor Ort bei dir', text: '' }), 'Vor Ort bei dir')
assert.equal(eintragsKurztext({ kind: 'work', label: '', text: '' }), 'Arbeit am Projekt')
assert.equal(eintragsKurztext({ kind: 'online', label: 'Online erledigt', text: 'Erste Zeile\n\nZweite Zeile mit Lücken' }), 'Erste Zeile')
assert.equal(eintragsKurztext({ kind: 'online', label: 'x', text: ' viel Leerraum ' }), 'viel Leerraum')
const lang = eintragsKurztext({ kind: 'online', label: 'x', text: 'Wort '.repeat(60) })
assert.ok(lang.length <= 91 && lang.endsWith('…') && !lang.includes(' '), lang)
assert.equal(eintragsKurztext(null), 'Arbeit am Projekt')
})
test('Zeitleiste: neueste zuerst, Datum TT.MM., ohne Datum ganz unten, keine Namen und Minuten', () => {
const a = auftrag(1, 'arbeit', '2026-10-01T00:00:00Z', [
eintrag('2026-10-03', 'Alt'), eintrag('2026-10-12', 'Neu', { person: 'Kenso', minutes: 90 }), eintrag('', 'Ohne Datum'), eintrag('2026-10-07', 'Mitte'),
])
const zeilen = zeitleistenZeilen(a, JETZT)
assert.deepEqual(zeilen.map((z) => [z.datum, z.text]), [['12.10.', 'Neu'], ['07.10.', 'Mitte'], ['03.10.', 'Alt'], ['', 'Ohne Datum']])
assert.deepEqual(Object.keys(zeilen[0]).sort(), ['datum', 'iso', 'text'])
assert.equal(zeilen[0].iso, '2026-10-12')
assert.deepEqual(zeitleistenZeilen({ entries: null }, JETZT), []); assert.deepEqual(zeitleistenZeilen(null, JETZT), [])
})