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>
This commit is contained in:
1 parent
eadb768d1a
commit
0db59d38e9
11 files changed
+376
-135
No files matched your search
+47
-3
@@ -47,11 +47,13 @@ const BEFORE = {
|
||||
}
|
||||
const SCALE = { 7: 0.25, 30: 1, 90: 3 } // Anteil der 30-Tage-Zahlen je Zeitraum
|
||||
const PAGES = [['/', 2480], ['/preise', 1310], ['/kurse', 1150], ['/routen', 960], ['/oeffnungszeiten', 640], ['/team', 410], ['/kontakt', 300], ['/impressum', 120], ['/datenschutz', 90]]
|
||||
// Der Zeitraum endet gestern (abgeschlossene Tage); heute steht getrennt in `today`
|
||||
function makeStats(range, over = {}) {
|
||||
const from = addDays(TODAY, -(range - 1))
|
||||
const from = addDays(TODAY, -range)
|
||||
const pfrom = addDays(from, -range)
|
||||
return {
|
||||
range, from, to: TODAY, firstDay: addDays(pfrom, -40), totals: TOTALS[range], series: daysOf(from, range, 231, Math.min(14, range - 1)),
|
||||
range, from, to: addDays(TODAY, -1), firstDay: addDays(pfrom, -40), totals: TOTALS[range], series: daysOf(from, range, 231, Math.min(14, range - 1)),
|
||||
today: { day: TODAY, pv: 42, visitors: 17, contacts: 2 },
|
||||
pages: PAGES.map(([key, count]) => ({ key, count: Math.round(count * SCALE[range]) })),
|
||||
refs: [{ key: 'google.com', count: 880 }, { key: '', count: 700 }, { key: 'instagram.com', count: 400 }, { key: 'l.facebook.com', count: 290 }].map((r) => ({ ...r, count: Math.round(r.count * SCALE[range]) })),
|
||||
countries: [{ key: 'DE', count: 6000 }, { key: 'AT', count: 600 }, { key: 'CH', count: 330 }],
|
||||
@@ -79,6 +81,8 @@ const SCENARIOS = {
|
||||
wenig: { project: project(), stats: (r) => makeStats(r, { totals: { ...TOTALS[r], pv: 12 } }) },
|
||||
dev: { project: project({ devMode: true }), stats: (r) => makeStats(r) },
|
||||
alt: { project: project(), stats: (r) => OLD(r) },
|
||||
stark: { project: project(), stats: (r) => makeStats(r, { series: daysOf(addDays(TODAY, -r), r, 2400, Math.min(14, r - 1)) }) },
|
||||
sehrStark: { project: project(), stats: (r) => makeStats(r, { series: daysOf(addDays(TODAY, -r), r, 12000, Math.min(14, r - 1)) }) },
|
||||
langerPfad: { project: project(), stats: (r) => makeStats(r, { pages: [{ key: longPath, count: 900 }, { key: '/kurz', count: 500 }], refs: [{ key: `${'h'.repeat(90)}.example.com`, count: 300 }] }) },
|
||||
fehler: { project: project(), stats: null },
|
||||
zwei: { projects: [project(), { ...baseProject, projectId: 'p2', projectName: 'Zweite Website mit einem wirklich sehr langen Namen, der nicht in eine Zeile passt', analyse: analyse({}) }], stats: (r) => makeStats(r) },
|
||||
@@ -168,7 +172,7 @@ try {
|
||||
await open('full')
|
||||
check(L('aktiv: Block "Das Wichtigste" mit "14 % mehr"'), (await text('.an-sum')).includes('Das Wichtigste') && (await text('.an-sum')).includes('14 % mehr'))
|
||||
check(L('aktiv: 4 Kacheln, 3 Chips ▲, 1 Chip ▼'), (await count('.an-kpis .an-kpi')) === 4 && (await count('.an-kpis .an-delta.up')) === 3 && (await count('.an-kpis .an-delta.down')) === 1 && (await text('.an-kpis')).includes('▲') && (await text('.an-kpis')).includes('▼'))
|
||||
check(L('aktiv: Kopfleiste mit Stand-Uhrzeit, Zeitraum 30 Tage gewählt, Reiter Übersicht gewählt'), /Analyse aktiv · Stand: heute, \d\d:\d\d Uhr/.test(await text('.an-status')) && (await page.getAttribute('.an-seg .on', 'data-range')) === '30' && (await page.getAttribute('.an-tabbtn[aria-selected="true"]', 'data-tab')) === 'uebersicht')
|
||||
check(L('aktiv: Kopfleiste mit "Heute bisher: 42 Aufrufe" und Stand-Uhrzeit, Zeitraum 30 Tage gewählt, Reiter Übersicht gewählt'), /^Analyse aktiv · Heute bisher: 42 Aufrufe · Stand: \d\d:\d\d Uhr$/.test(await text('.an-status')) && (await page.getAttribute('.an-seg .on', 'data-range')) === '30' && (await page.getAttribute('.an-tabbtn[aria-selected="true"]', 'data-tab')) === 'uebersicht')
|
||||
check(L('kein_horizontaler_scroll aktiv'), await noScroll())
|
||||
// Raster: Desktop 4 Kacheln nebeneinander, Handy 2 x 2; Diagramm und Kontaktkarte gleich hoch (Desktop)
|
||||
const tiles = await page.$$eval('.an-kpis .an-kpi', (els) => els.map((e) => { const r = e.getBoundingClientRect(); return { x: Math.round(r.left), y: Math.round(r.top), w: Math.round(r.width) } }))
|
||||
@@ -208,6 +212,28 @@ try {
|
||||
}
|
||||
await page.waitForFunction(() => document.querySelector('.an-tip')?.hidden === true)
|
||||
check(L('Diagramm-Blase: verschwindet wieder, kein Punkt bleibt markiert'), (await count('.an-dot.on')) === 0)
|
||||
// Achsenbeschriftung: gerendert ca. 11-12 px (viewBox 640 skaliert mit der Breite), nichts ragt aus der Karte, x-Beschriftungen überlappen nicht
|
||||
const axis = () => page.evaluate(() => {
|
||||
const svg = document.querySelector('.an-chart')
|
||||
const scale = svg.getBoundingClientRect().width / 640
|
||||
const box = document.querySelector('.an-chartbox').getBoundingClientRect()
|
||||
const texts = [...svg.querySelectorAll('.an-axis')].map((t) => ({ r: t.getBoundingClientRect(), px: parseFloat(getComputedStyle(t).fontSize) * scale, anchor: t.getAttribute('text-anchor'), s: t.textContent }))
|
||||
const xs = texts.filter((t) => t.anchor === 'middle').sort((a, b) => a.r.left - b.r.left)
|
||||
return {
|
||||
px: Math.min(...texts.map((t) => t.px)), pxMax: Math.max(...texts.map((t) => t.px)),
|
||||
inside: texts.every((t) => t.r.left >= box.left + 1 && t.r.right <= box.right - 1),
|
||||
gap: xs.every((t, i) => !i || t.r.left >= xs[i - 1].r.right + 4),
|
||||
y: texts.filter((t) => t.anchor === 'end').map((t) => t.s).join(','),
|
||||
}
|
||||
})
|
||||
const ax = await axis()
|
||||
check(L(`Achsenbeschriftung ca. 11-12 px (${ax.px.toFixed(1)} px), liegt in der Karte, x-Werte überlappen nicht`), ax.px >= 10.5 && ax.pxMax <= 13 && ax.inside && ax.gap)
|
||||
const faint = await page.evaluate(() => getComputedStyle(document.querySelector('.an-credit')).color)
|
||||
check(L(`Hilfstext "--an-faint" kontrastreich (${faint})`), Number(/rgba?\([^)]*?,\s*([\d.]+)\)$/.exec(faint)?.[1] ?? 1) >= 0.55)
|
||||
if (touch) {
|
||||
const heights = await page.evaluate(() => Object.fromEntries(['.an-tabbtn', '.an-seg button', '.an-ltab', '.an-more', '.an-teaser .an-btn'].map((sel) => [sel, Math.min(...[...document.querySelectorAll(sel)].map((e) => e.getBoundingClientRect().height))])))
|
||||
check(L(`Tippflächen mindestens 40 px: ${Object.entries(heights).map(([k, v]) => `${k} ${v.toFixed(0)}`).join(', ')}`), Object.values(heights).every((h) => h >= 39.5))
|
||||
}
|
||||
// Ranglisten: Umschalter und "Alle anzeigen" ändern nur ihre Karte
|
||||
check(L('Rangliste: 6 Zeilen und "Alle anzeigen"'), (await count('[data-rank="seen"] .an-lr')) === 6 && (await count('[data-rank="seen"] [data-rank-more]')) === 1)
|
||||
await page.click('[data-rank="seen"] [data-rank-more]')
|
||||
@@ -255,6 +281,16 @@ try {
|
||||
const settings = await text('#analyse-body')
|
||||
check(L('Reiter Einstellungen: "Analyse ausschalten" sichtbar, "aktiv seit"'), (await page.isVisible('#an-off-ask')) && settings.includes('Analyse ausschalten') && /aktiv seit 12\.08\.2026/.test(settings) && settings.includes('Fassung 2026-10-02') && (await count('.an-docs a')) === 2 && (await count('.an-swatch')) === 1 && (await count('.an-top .an-seg')) === 0)
|
||||
check(L('kein_horizontaler_scroll einstellungen'), await noScroll())
|
||||
const errColor = await page.evaluate(() => {
|
||||
const p = document.createElement('p')
|
||||
p.className = 'error'
|
||||
p.textContent = 'x'
|
||||
document.querySelector('#an-off').appendChild(p)
|
||||
const c = getComputedStyle(p).color
|
||||
p.remove()
|
||||
return c
|
||||
})
|
||||
check(L(`Fehlertext beim Ausschalten rot (${errColor})`), errColor === 'rgb(252, 165, 165)')
|
||||
await shot('einstellungen')
|
||||
await page.click('#an-off-ask')
|
||||
await page.click('#an-off-yes')
|
||||
@@ -311,6 +347,7 @@ try {
|
||||
await shot('dev')
|
||||
|
||||
await open('alt')
|
||||
check(L('alt: kein "Heute bisher" (alter Collector), Stand bleibt'), /^Analyse aktiv · Stand: \d\d:\d\d Uhr$/.test(await text('.an-status')))
|
||||
check(L('alt: keine Chips, keine pageerror'), (await count('.an-delta')) === 0 && (await count('.an-prev')) === 0 && errors.length === 0 && (await text('.an-kpis')).includes('612'))
|
||||
await shot('alt')
|
||||
|
||||
@@ -323,6 +360,13 @@ try {
|
||||
check(L('fehler: "Neu laden" fragt erneut an'), statsRequests.length === requestsBefore + 1)
|
||||
await shot('fehler')
|
||||
|
||||
for (const [sc, label5] of [['stark', 'vierstellige'], ['sehrStark', 'fünfstellige']]) {
|
||||
await open(sc)
|
||||
const big = await axis()
|
||||
check(L(`${label5} y-Werte (${big.y}) nicht abgeschnitten, x-Werte überlappen nicht, Schrift ${big.px.toFixed(1)} px`), big.inside && big.gap && big.px >= 10.5 && big.pxMax <= 13)
|
||||
await shot(sc === 'stark' ? 'skala-4-stellig' : 'skala-5-stellig')
|
||||
}
|
||||
|
||||
await open('langerPfad')
|
||||
const name = await page.evaluate(() => { const n = document.querySelector('[data-rank="seen"] .an-name'); return { sw: n.scrollWidth, cw: n.clientWidth, title: n.title } })
|
||||
check(L(`langer Pfad${touch ? ', 402 px' : ''}: document.scrollingElement.scrollWidth <= innerWidth`), (await noScroll()) && name.sw > name.cw && name.title.length > 100)
|
||||
|
||||
Reference in new issue
Block a user