Files
bin/finance/tests/js/sort.test.js
T
rdmsandClaude Opus 5.5 4458300f43 feat: Tabellen per Spaltenkopf sortierbar (A11-T4)
Neues static/sort.js sortiert Tabellen clientseitig per Klick auf den
Spaltenkopf (⇅/▲/▼): Datum TT.MM.JJJJ, deutsche Zahlen/Beträge, sonst
Text (Intl.Collator 'de'). Sortierwert optional per td[data-sort]
(Punkt-Dezimal/ISO) überschreibbar, th[data-nosort] bekommt keinen
Button, table[data-sort-mode="server"] wird übersprungen (Buchungen
sortiert serverseitig, folgt in Task 5). Bearbeiten-/Tilgungsplan-
Zeilen (tr[data-sort-follow]) wandern mit ihrer Vorzeile mit.
Initialisierung über htmx.onLoad, greift auch bei nachgeladenen
Fragmenten.

Templates entsprechend annotiert (Planung, Szenarien, Admin, Index,
Salden, Import-Liste, Buchungen); Salden-Gesamtzeile dafür in ein
tfoot verschoben. test_gui.py::test_gui_zeigt_deutsche_betragsformate
angepasst, da das neue machinenlesbare data-sort-Attribut bewusst
Punkt-Dezimal führt (Interface-Konvention) - die Prüfung auf deutsches
Format im sichtbaren Text bleibt bestehen.

JS-Unit-/DOM-Tests unter tests/js (node --test, DOM-Teil optional via
jsdom), pytest-Wrapper skip ohne node (Fable-Gate führt sie per
Podman aus). Suite: 248 passed, 3 skipped (vorher 242/2).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 19:22:34 +02:00

73 lines
2.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
const test = require('node:test');
const assert = require('node:assert');
const S = require('../../app/static/sort.js');
const items = (vals, ds) => vals.map((v, i) => ({ key: S.sortKey(v, !!ds), index: i, label: v }));
const labels = (arr) => arr.map((i) => i.label);
test('Datum TT.MM.JJJJ', () => {
const k = S.sortKey('05.03.2026', false);
assert.equal(k.kind, 'date');
assert.equal(k.value, 20260305);
});
test('deutsche Beträge und Zahlen', () => {
assert.equal(S.sortKey('-1.234,56 €', false).value, -1234.56);
assert.equal(S.sortKey('5,00 %', false).value, 5);
assert.equal(S.sortKey('15', false).kind, 'number');
assert.equal(S.sortKey('1.234', false).value, 1234);
});
test('data-sort: Punkt-Dezimal und ISO-Datum', () => {
assert.equal(S.sortKey('-1234.56', true).value, -1234.56);
assert.equal(S.sortKey('2026-06-30', true).kind, 'text');
});
test('Leerwerte sind null', () => {
assert.equal(S.sortKey('', false), null);
assert.equal(S.sortKey(' ', false), null);
assert.equal(S.sortKey('–', false), null);
assert.equal(S.sortKey(undefined, false), null);
});
test('Zahlen numerisch statt alphabetisch', () => {
assert.deepEqual(labels(S.sortItems(items(['10,00', '9,00', '-100,00']), 'asc')),
['-100,00', '9,00', '10,00']);
assert.deepEqual(labels(S.sortItems(items(['10,00', '9,00', '-100,00']), 'desc')),
['10,00', '9,00', '-100,00']);
});
test('Datum chronologisch', () => {
assert.deepEqual(labels(S.sortItems(items(['01.02.2026', '15.01.2026', '31.12.2025']), 'asc')),
['31.12.2025', '15.01.2026', '01.02.2026']);
});
test('Leerwerte immer am Ende, beide Richtungen', () => {
const v = ['01.02.2026', '–', '15.01.2026', ''];
assert.deepEqual(labels(S.sortItems(items(v), 'asc')), ['15.01.2026', '01.02.2026', '–', '']);
assert.deepEqual(labels(S.sortItems(items(v), 'desc')), ['01.02.2026', '15.01.2026', '–', '']);
});
test('gemischte Typen -> Text', () => {
assert.equal(S.columnKind([S.sortKey('10,00'), S.sortKey('abc'), null]), 'text');
assert.equal(S.columnKind([S.sortKey('10,00'), null]), 'number');
});
test('Text deutsch (Umlaute, Groß/klein)', () => {
assert.deepEqual(labels(S.sortItems(items(['Zahnarzt', 'Ärztin', 'miete']), 'asc')),
['Ärztin', 'miete', 'Zahnarzt']);
});
test('stabil bei gleichen Werten', () => {
const it = [{ key: S.sortKey('5'), index: 0, label: 'a' }, { key: S.sortKey('5'), index: 1, label: 'b' }];
assert.deepEqual(labels(S.sortItems(it, 'desc')), ['a', 'b']);
});
test('groupRows: Folgezeilen hängen an der Vorzeile', () => {
const rows = [{ id: 'a' }, { id: 'a-edit', f: true }, { id: 'a-plan', f: true },
{ id: 'b' }, { id: 'b-edit', f: true }];
assert.deepEqual(S.groupRows(rows, (r) => !!r.f).map((g) => g.map((r) => r.id)),
[['a', 'a-edit', 'a-plan'], ['b', 'b-edit']]);
});