v0.8.82-beta - "Poort {port} is vrij" stond er letterlijk
All checks were successful
Deploy server-up (dev) / deploy (push) Successful in 17m4s
All checks were successful
Deploy server-up (dev) / deploy (push) Successful in 17m4s
- De poortcontrole werd aangesloten op vertaalsleutels die er al waren, en twee
daarvan verwachten een plaatshouder die ik niet meegaf: port_free wil {port},
port_in_use wil {port} en {by}
- Nieuwe test die elke t('sleutel', {...}) in de interface vergelijkt met de
plaatshouders in de Nederlandse tekst, in beide richtingen en ook voor
aanroepen zonder variabelen. Die laatste toevoeging was nodig: de eerste
versie van de test liet juist de fout passeren waar hij voor gemaakt was
- De test kent nu ook de verkorte objectnotatie van JavaScript ({name} betekent
{name: name}); zonder dat meldde hij vier bestaande aanroepen ten onrechte
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9eqpADJSRs49SoGGr4NAy
This commit is contained in:
parent
d7c4682cc7
commit
b6319e8fdd
4 changed files with 103 additions and 3 deletions
13
CHANGELOG.md
13
CHANGELOG.md
|
|
@ -1,3 +1,16 @@
|
||||||
|
# v0.8.82-beta — "Poort {port} is vrij" stond er letterlijk
|
||||||
|
|
||||||
|
De poortcontrole werd aangesloten op vertaalsleutels die er al waren, en twee
|
||||||
|
daarvan verwachten een plaatshouder die niet werd meegegeven: `port_free` wil
|
||||||
|
`{port}`, `port_in_use` wil `{port}` én `{by}`. In beeld stond dus letterlijk
|
||||||
|
"Poort {port} is vrij".
|
||||||
|
|
||||||
|
Er draait nu een test die elke `t('sleutel', { … })` in de interface naloopt en
|
||||||
|
vergelijkt met de plaatshouders in de Nederlandse tekst — in beide richtingen,
|
||||||
|
en ook voor aanroepen zónder variabelen. Dat laatste was nodig: de eerste versie
|
||||||
|
van die test keek alleen naar aanroepen die wél iets meegaven, en liet de fout
|
||||||
|
waar hij voor gemaakt was gewoon passeren.
|
||||||
|
|
||||||
# v0.8.81-beta — Poorten kiezen zonder gokken, en installeren op een telefoon
|
# v0.8.81-beta — Poorten kiezen zonder gokken, en installeren op een telefoon
|
||||||
|
|
||||||
**De poortcontrole zat er wel, maar werd nooit gevraagd.** `/api/ports/check`
|
**De poortcontrole zat er wel, maar werd nooit gevraagd.** `/api/ports/check`
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
0.8.81-beta
|
0.8.82-beta
|
||||||
|
|
|
||||||
|
|
@ -3282,11 +3282,15 @@ function app() {
|
||||||
if (!c.free) {
|
if (!c.free) {
|
||||||
return c.used_by
|
return c.used_by
|
||||||
? this.t('port_used_by', { port: c.port, container: c.used_by })
|
? this.t('port_used_by', { port: c.port, container: c.used_by })
|
||||||
: this.t('port_in_use', { port: c.port });
|
// `by` staat in de tekst voor het geval we wél weten waardoor; hier
|
||||||
|
// weten we dat niet, dus leeg. Weglaten zou '{by}' in beeld zetten.
|
||||||
|
: this.t('port_in_use', { port: c.port, by: '' });
|
||||||
}
|
}
|
||||||
// Zonder de hulpcontainer zien we alleen Docker; dan is "vrij" een
|
// Zonder de hulpcontainer zien we alleen Docker; dan is "vrij" een
|
||||||
// aanname en dat hoort de melding te zeggen.
|
// aanname en dat hoort de melding te zeggen.
|
||||||
return c.host_checked ? this.t('port_free') : this.t('port_free_docker_only');
|
return c.host_checked
|
||||||
|
? this.t('port_free', { port: c.port })
|
||||||
|
: this.t('port_free_docker_only');
|
||||||
},
|
},
|
||||||
// Bij het openen van een stap meteen controleren wat er in de poortvelden
|
// Bij het openen van een stap meteen controleren wat er in de poortvelden
|
||||||
// staat, in plaats van te wachten tot iemand iets typt.
|
// staat, in plaats van te wachten tot iemand iets typt.
|
||||||
|
|
|
||||||
|
|
@ -128,3 +128,86 @@ def test_auditnamen_bestaan_in_beide_talen():
|
||||||
nl, en = _laad("nl"), _laad("en")
|
nl, en = _laad("nl"), _laad("en")
|
||||||
alleen_nl = sorted(k for k in nl if k.startswith("audit_") and k not in en)
|
alleen_nl = sorted(k for k in nl if k.startswith("audit_") and k not in en)
|
||||||
assert not alleen_nl, f"ontbreken in en.json: {alleen_nl}"
|
assert not alleen_nl, f"ontbreken in en.json: {alleen_nl}"
|
||||||
|
|
||||||
|
|
||||||
|
def _meegegeven(blok: str) -> set[str]:
|
||||||
|
"""De sleutels uit een `{…}` in een t()-aanroep.
|
||||||
|
|
||||||
|
JavaScript kent twee vormen: `{port: c.port}` en de verkorte `{name}`, die
|
||||||
|
hetzelfde betekent als `{name: name}`. Alleen op een dubbele punt zoeken
|
||||||
|
mist die tweede, en dan meldt de test fouten die er niet zijn.
|
||||||
|
"""
|
||||||
|
uit, huidig, diepte = set(), "", 0
|
||||||
|
for teken in blok + ",":
|
||||||
|
if teken in "([{":
|
||||||
|
diepte += 1
|
||||||
|
elif teken in ")]}":
|
||||||
|
diepte -= 1
|
||||||
|
if teken == "," and diepte == 0:
|
||||||
|
deel = huidig.strip()
|
||||||
|
if deel:
|
||||||
|
naam = deel.split(":", 1)[0].strip()
|
||||||
|
if re.fullmatch(r"\w+", naam):
|
||||||
|
uit.add(naam)
|
||||||
|
huidig = ""
|
||||||
|
else:
|
||||||
|
huidig += teken
|
||||||
|
return uit
|
||||||
|
|
||||||
|
|
||||||
|
def _t_aanroepen(html: str):
|
||||||
|
"""Elke `t('sleutel', { … })` met de meegegeven sleutels."""
|
||||||
|
for m in re.finditer(r"\bt\(\s*'([a-z0-9_]+)'\s*,\s*\{([^{}]*)\}", html):
|
||||||
|
yield m.group(1), _meegegeven(m.group(2))
|
||||||
|
|
||||||
|
|
||||||
|
def test_elke_plaatshouder_wordt_ook_meegegeven():
|
||||||
|
"""`t('port_free')` op een tekst met `{port}` erin zet letterlijk "{port}"
|
||||||
|
in beeld.
|
||||||
|
|
||||||
|
Dat is precies wat er gebeurde toen de poortcontrole werd aangesloten op
|
||||||
|
sleutels die er al waren: `port_free` verwacht `{port}`, `port_in_use`
|
||||||
|
verwacht `{port}` én `{by}`, en die werden niet meegegeven.
|
||||||
|
"""
|
||||||
|
nl = _laad("nl")
|
||||||
|
html = (WORTEL / "templates" / "index.html").read_text(encoding="utf-8")
|
||||||
|
aanroepen = list(_t_aanroepen(html))
|
||||||
|
assert aanroepen, "geen t()-aanroepen met variabelen gevonden — vorm veranderd?"
|
||||||
|
|
||||||
|
fouten = []
|
||||||
|
for sleutel, gegeven in aanroepen:
|
||||||
|
tekst = nl.get(sleutel)
|
||||||
|
if tekst is None:
|
||||||
|
continue # andere test dekt onbekende sleutels
|
||||||
|
ontbreekt = set(re.findall(r"\{(\w+)\}", tekst)) - gegeven
|
||||||
|
if ontbreekt:
|
||||||
|
fouten.append(f"{sleutel}: mist {sorted(ontbreekt)}")
|
||||||
|
|
||||||
|
# En het geval dat de eerste versie van deze test miste: een aanroep zónder
|
||||||
|
# variabelen op een tekst die er wél verwacht. Dat was precies de fout —
|
||||||
|
# `t('port_free')` op "Poort {port} is vrij".
|
||||||
|
for m in re.finditer(r"\bt\(\s*'([a-z0-9_]+)'\s*\)", html):
|
||||||
|
sleutel = m.group(1)
|
||||||
|
tekst = nl.get(sleutel)
|
||||||
|
if tekst is None:
|
||||||
|
continue
|
||||||
|
nodig = sorted(set(re.findall(r"\{(\w+)\}", tekst)))
|
||||||
|
if nodig:
|
||||||
|
fouten.append(f"{sleutel}: aangeroepen zonder {nodig}")
|
||||||
|
assert not fouten, fouten
|
||||||
|
|
||||||
|
|
||||||
|
def test_geen_plaatshouder_zonder_tekst():
|
||||||
|
"""Andersom: een sleutel meegeven die niet in de tekst staat is meestal een
|
||||||
|
hernoeming die half is doorgevoerd."""
|
||||||
|
nl = _laad("nl")
|
||||||
|
html = (WORTEL / "templates" / "index.html").read_text(encoding="utf-8")
|
||||||
|
fouten = []
|
||||||
|
for sleutel, gegeven in _t_aanroepen(html):
|
||||||
|
tekst = nl.get(sleutel)
|
||||||
|
if tekst is None:
|
||||||
|
continue
|
||||||
|
over = gegeven - set(re.findall(r"\{(\w+)\}", tekst))
|
||||||
|
if over:
|
||||||
|
fouten.append(f"{sleutel}: geeft {sorted(over)} mee, staat niet in de tekst")
|
||||||
|
assert not fouten, fouten
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue