Files
ClaudeandClaude Opus 5.5 0db59d38e9 Analyse: Zeitraum = abgeschlossene Tage, "Heute bisher" getrennt, Feinschliff
Final-Review-Fixwelle der Cockpit-Übersicht:

- F1 Collector: Der Zeitraum sind die range Kalendertage bis gestern (Berliner Tag),
  der Vorzeitraum die range Tage davor; heute steht getrennt in today { day, pv, visitors,
  contacts }. Kein Einbruch mehr am rechten Rand von Diagramm und Mini-Verläufen,
  kein falsches "x % weniger" tagsüber. Portal: weekdayStats bekommt die ganze Reihe
  (completedDays entfällt), "Vergleich ab" = firstDay + 2 x range, Kopfzeile
  "Analyse aktiv · Heute bisher: N Aufrufe · Stand: HH:MM Uhr" (alter Collector ohne today
  bleibt ohne "Heute bisher").
- F2 "direkt kontaktiert" zählt nur Telefon, E-Mail und WhatsApp (auch im Vorzeitraum),
  Links nach außen nicht.
- F3 Mini-Verlauf "Ø Zeit pro Seite": Tage ohne Messung sind Lücken statt 0-Tälern.
- F4 Fehlerfarbe beim Ausschalten rot, --an-faint auf 55 %, Achsenschrift per
  Container-Abfragen auf 11-12 px (y-Rand wächst mit der Stellenzahl, x-Beschriftungen mit
  Mindestabstand), Ranglisten-Anteile an allen Seitenaufrufen, Tippflächen bis 880 px
  mindestens 40 px hoch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 21:34:43 +00:00

80 lines
4.0 KiB
JavaScript

import { test } from 'node:test'
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import { contrastOk, BANNER_TEXT } from '../analytics/client/core.js'
import { BANNER_PREVIEW_TEXT, controllerLines, bannerColor, chartModel, countryName, flagEmoji, niceMax, shortDay, barRows, refLabel } from '../public/analyse-view.js'
test('contrast und bannerColor stimmen mit core.js ueberein', () => {
for (const c of ['#000000', '#ffffff', '#1d4ed8', '#047857', '#b91c1c', '#7c3aed', '#c2410c', '#ffcc00', '#9d3dfe', 'x', '#12']) {
const { contrastOk: portal } = { contrastOk: bannerColor(c) !== '#111111' || c.toLowerCase() === '#111111' }
assert.equal(portal, contrastOk(c) || c.toLowerCase() === '#111111', c)
}
assert.equal(bannerColor('#ffcc00'), '#111111')
assert.equal(bannerColor('#1D4ED8'), '#1d4ed8')
assert.equal(bannerColor('nonsense'), '#111111')
})
test('Land und Flagge', () => {
assert.equal(countryName('DE'), 'Deutschland')
assert.equal(countryName(''), 'Unbekannt')
assert.equal(flagEmoji('de'), '🇩🇪')
assert.equal(flagEmoji(''), '')
assert.equal(flagEmoji('<b>'), '')
assert.equal(refLabel(''), 'Direkt')
})
test('Diagramm-Skalierung', () => {
assert.equal(niceMax(0), 1)
assert.equal(niceMax(7), 10)
assert.equal(niceMax(130), 200)
assert.equal(shortDay('2026-10-01'), '01.10.')
const series = Array.from({ length: 30 }, (_, i) => ({ day: `2026-09-${String(i + 1).padStart(2, '0')}`, pv: i }))
const m = chartModel(series)
assert.equal(m.points.length, 30)
assert.equal(m.top, 50)
assert.ok(m.points[0].x < m.points[29].x)
assert.ok(m.points.every((p) => p.x >= m.pad.l && p.x <= m.width - m.pad.r && p.y >= m.pad.t && p.y <= m.height - m.pad.b))
assert.ok(m.xLabels.length <= 7 && m.xLabels.length >= 2)
assert.equal(chartModel([]).points.length, 0)
assert.equal(chartModel([{ day: '2026-10-01', pv: 3 }]).points.length, 1)
// labelGap: die vorletzte Beschriftung rückt weg, wenn sie der letzten zu nahe kommt (30 Tage: 29.09. ist 79 Einheiten vor 03.10.)
const labels = (opts) => chartModel(series, { width: 640, ...opts }).xLabels.map((l) => l.label)
assert.equal(labels({}).length, 7)
const wide = chartModel(series, { labelGap: 90 }).xLabels
assert.equal(wide.length, 6)
assert.ok(wide.every((l, i, a) => !i || l.x - a[i - 1].x >= 90))
assert.equal(wide[wide.length - 1].label, '30.09.') // letzter Tag bleibt
})
test('Balkenzeilen', () => {
assert.deepEqual(barRows([{ key: 'a', count: 4 }, { key: 'b', count: 2 }], (k) => k.toUpperCase()), [
{ key: 'a', label: 'A', count: 4, pct: 100 },
{ key: 'b', label: 'B', count: 2, pct: 50 },
])
assert.deepEqual(barRows(undefined, String), [])
})
test('Portal: Analyse-Ansicht ist verdrahtet', () => {
const app = readFileSync(new URL('../public/app.js', import.meta.url), 'utf8')
const html = readFileSync(new URL('../public/dashboard.html', import.meta.url), 'utf8')
assert.match(app, /analyse: 'analyse'/)
assert.match(app, /APP_PARENT = \{ news: 'funktionen', analyse: 'funktionen'/)
assert.match(html, /id="view-analyse"/)
assert.match(html, /id="analyse-body"/)
assert.match(app, /analyse: \(\) => import\('\/analyse\.js'\)/)
const an = readFileSync(new URL('../public/analyse.js', import.meta.url), 'utf8')
assert.doesNotMatch(an, /\bconfirm\(/)
const fn = readFileSync(new URL('../public/funktionen.js', import.meta.url), 'utf8')
assert.doesNotMatch(fn, /function renderAnalyseStats|function renderAnalyseHeat/)
})
test('Portal-Vorschau: Banner-Text ist der des Clients', () => {
assert.equal(BANNER_PREVIEW_TEXT, BANNER_TEXT)
})
test('controllerLines: Firma, Anschrift falls vorhanden, E-Mail', () => {
assert.deepEqual(controllerLines({ company: 'Muster GmbH', address: 'Weg 1, 12345 Ort', email: 'info@muster.de' }), ['Muster GmbH', 'Weg 1, 12345 Ort', 'info@muster.de'])
assert.deepEqual(controllerLines({ company: 'Muster GmbH', address: '', email: 'info@muster.de' }), ['Muster GmbH', 'info@muster.de'])
assert.deepEqual(controllerLines(undefined), [])
})