CluPilotCloud/tests/Feature/ChartLiveUpdateTest.php

52 lines
2.3 KiB
PHP

<?php
/**
* Die Chart.js-Instanz darf nicht im reaktiven Alpine-Objekt liegen.
*
* Gefunden am 1. August 2026 am ersten Live-Chart der Konsole: die Zahlen neben
* der Kurve wanderten, die Kurve nicht. `chart.update()` starb still im
* Legenden-Plugin mit
*
* TypeError: Cannot set properties of undefined (setting 'fullSize')
*
* Alles, was `Alpine.data()` zurückgibt, wird reaktiv — also in ein Proxy
* gehüllt. Chart.js' Plugin-Innenleben überlebt das nicht. Gemessen im Browser,
* nicht geschlossen: dieselbe Instanz wirft über das Proxy und läuft über
* `Alpine.raw()`.
*
* Warum das jahrelang niemandem auffiel: bis zum Live-Chart rief kein einziger
* Chart in diesem Repo je `update()`. Konstruieren und Erstzeichnen gehen durch
* die Hülle noch. Wer den nächsten Live-Chart baut, würde denselben Nachmittag
* verlieren — deshalb steht die Regel hier und nicht nur im Kommentar.
*
* Eine Textprüfung, weil dieses Repo keine JS-Testumgebung hat. Sie prüft die
* eine Zeile, an der die Entscheidung hängt.
*/
it('keeps the chart instance out of alpine reactive state', function () {
$js = file_get_contents(base_path('resources/js/app.js'));
$start = strpos($js, "Alpine.data('chart'");
expect($start)->not->toBeFalse('Die Alpine-Komponente `chart` gibt es nicht mehr — dieser Test braucht einen neuen Anker.');
// Der Rumpf bis zur nächsten Alpine-Komponente.
$next = strpos($js, 'Alpine.data(', $start + 20);
$body = substr($js, $start, $next === false ? 4000 : $next - $start);
expect($body)
// In der Closure, nicht am Objekt.
->toContain('let instance = null')
// `instance: null` als Objekt-Eigenschaft ist genau der Rückfall.
->not->toMatch('/^\s*instance:\s/m')
->not->toContain('this.instance');
});
it('still offers the live update path the host page depends on', function () {
$js = file_get_contents(base_path('resources/js/app.js'));
$blade = file_get_contents(base_path('resources/views/components/ui/chart.blade.php'));
// Ohne diesen Weg zeigt die Lastkurve für immer die Werte des Seitenaufrufs.
expect($js)->toContain('window.addEventListener(updateOn')
->and($js)->toContain("instance.update('none')")
->and($blade)->toContain('updateOn');
});