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>
73 lines
2.6 KiB
Python
73 lines
2.6 KiB
Python
import re
|
|
from datetime import date
|
|
from decimal import Decimal
|
|
|
|
from app.models.tables import (Account, Category, Loan, PlannedItem,
|
|
RecurringItem, Scenario, ScenarioModifier,
|
|
ScenarioPlannedItem)
|
|
|
|
|
|
def _login(client):
|
|
client.post("/login", data={"username": "admin", "password": "geheim"})
|
|
|
|
|
|
def _follow_ids(html):
|
|
return re.findall(r'<tr id="([a-z]+-(?:edit|plan)-\d+)"[^>]*data-sort-follow', html)
|
|
|
|
|
|
def test_base_bindet_sort_js_ein(client):
|
|
_login(client)
|
|
assert '<script src="/static/sort.js"></script>' in client.get("/").text
|
|
|
|
|
|
def test_planung_folgezeilen_markiert(client, db):
|
|
_login(client)
|
|
c = Category(name="K")
|
|
db.add(c)
|
|
db.flush()
|
|
db.add_all([
|
|
RecurringItem(name="R", amount=Decimal("-1.00"), rhythm="monthly", due_day=1, category_id=c.id),
|
|
PlannedItem(name="P", amount=Decimal("-1.00"), due=date(2030, 1, 1)),
|
|
Loan(name="L", principal=Decimal("100.00"), annual_rate_pct=Decimal("1"),
|
|
term_months=2, payout_date=date(2030, 1, 1), repayment_type="annuity"),
|
|
])
|
|
db.commit()
|
|
kinds = sorted(i.split("-")[0] + "-" + i.split("-")[1] for i in _follow_ids(client.get("/planung").text))
|
|
assert kinds == ["loan-edit", "loan-plan", "pln-edit", "rec-edit"]
|
|
|
|
|
|
def test_szenarien_und_admin_folgezeilen_markiert(client, db):
|
|
_login(client)
|
|
c = Category(name="K")
|
|
sc = Scenario(name="S", description="")
|
|
db.add_all([c, sc])
|
|
db.flush()
|
|
db.add_all([
|
|
ScenarioModifier(scenario_id=sc.id, target_type="category", target_id=c.id,
|
|
kind="percent", value=Decimal("10")),
|
|
ScenarioPlannedItem(scenario_id=sc.id, name="E", amount=Decimal("-1.00"), due=date(2030, 1, 1)),
|
|
])
|
|
db.commit()
|
|
assert sorted(i.rsplit("-", 1)[0] for i in _follow_ids(client.get("/szenarien").text)) == ["mod-edit", "spi-edit"]
|
|
assert [i.rsplit("-", 1)[0] for i in _follow_ids(client.get("/admin").text)] == ["cat-edit"]
|
|
|
|
|
|
def test_salden_gesamtzeile_im_tfoot(client, db):
|
|
_login(client)
|
|
db.add(Account(bank="t", iban="DE-S-1", name="Konto", type="giro"))
|
|
db.commit()
|
|
html = client.get("/salden").text
|
|
assert re.search(r'<tfoot>\s*<tr class="total-row">', html)
|
|
|
|
|
|
def test_index_aktionsspalten_ohne_sortierung(client):
|
|
_login(client)
|
|
html = client.get("/").text
|
|
assert "<th data-nosort>Umbenennen</th>" in html
|
|
assert "<th data-nosort>Anker pflegen</th>" in html
|
|
|
|
|
|
def test_buchungen_tabelle_ist_serverseitig(client):
|
|
_login(client)
|
|
assert 'data-sort-mode="server"' in client.get("/buchungen").text
|