UX-13 P2 review fixes (PR #145, comment 1880)
1. Discard reloads /config/model: after a stale save the page kept the
new fingerprint with the old values, so Discard + Save overwrote the
other surface's change without a 409.
2. Removing cards back to one restores the stored inverter shape, so a
flat site stays flat after add + remove.
3. A choice the page can only keep ("Other time windows") stays offered
while it is the stored value.
4. A load locks every locked value again.
5. at_zero() needs an accepted 0 W write; before the first one Home says
"Going to 0 W" (the heartbeat writes every heartbeat_s, so it clears).
6. A field with a unit drops the translation's "(W)" from its label: the
peak limit read "Peak limit (W)" beside a kW value.
7. A mirrored choice lists the mirrored field's keys, so a search for
static_profile finds the contract on a fixed-price site.
8. test_settings: a save refused by ui_to_options writes nothing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -103,8 +103,12 @@ check(f"every slider and locked value has its range from the schema "
|
||||
f"({len(_ranged)})", all(lo is not None and hi is not None
|
||||
for _k, lo, hi in _ranged)
|
||||
and ("peak_cap_w", 500.0, 15000.0) in _ranged)
|
||||
check("the peak limit is a kW slider over a value stored in W",
|
||||
field(FIELD, "peak_cap_w")["scale"] == 1000)
|
||||
check("the peak limit is a kW slider over a value stored in W, and its label "
|
||||
"does not say W (the unit is shown beside the control)",
|
||||
field(FIELD, "peak_cap_w")["scale"] == 1000
|
||||
and field(FIELD, "peak_cap_w")["label"] == "Peak limit"
|
||||
and not [f["key"] for _g, f in _flat
|
||||
if f["unit"] and re.search(r"\([^()\s]{1,6}\)", f["label"])])
|
||||
_choice = [f for _g, f in _flat
|
||||
if f["control"] in (L.SEGMENTED, L.SELECT, L.CHOICE)]
|
||||
check("every choice control has choices with words",
|
||||
@@ -139,7 +143,8 @@ check("the inverter cards carry their count, and adding one makes a list",
|
||||
check("a page-only field names the option keys it writes, for the grey key "
|
||||
"line", _reg["writes"] == ["price_taxes_profile"]
|
||||
and field(FIELD, "ui.solar")["writes"]
|
||||
== ["pv_entities", "pv_entity", "pv_invert_entities"])
|
||||
== ["pv_entities", "pv_entity", "pv_invert_entities"]
|
||||
and "static_profile" in field(FIELD, "ui.contract")["writes"])
|
||||
check("without a Supervisor schema the page still draws, with no ranges",
|
||||
len(uipage.layout([], {})["groups"]) == 6)
|
||||
|
||||
@@ -183,6 +188,18 @@ check("a locked value shows no input until 'Change' is pressed",
|
||||
and "UNLOCKED.has(id)) { open(); return box; }" in JS)
|
||||
check("nothing is saved while the add-on's own checks refuse the model",
|
||||
"if (MSG.some((m) => m.tier === \"bad\")) { toast(" in JS)
|
||||
check("Discard reloads the stored settings (after a stale save the page's "
|
||||
"copy is no longer what is stored)",
|
||||
re.search(r'"#sb-discard"\)\.addEventListener\("click", async \(\) => '
|
||||
r'\{\s*await load\(\);', JS) is not None)
|
||||
check("a load locks every locked value again, and a choice the page can only "
|
||||
"keep stays offered while it is the stored value",
|
||||
"UNLOCKED.clear();" in JS.split("async function load")[1].split("render();")[0]
|
||||
and "same(c[0], was)" in JS)
|
||||
check("removing cards back to one restores the stored shape (a flat inverter "
|
||||
"is not turned into a list of one)",
|
||||
"if (V[f.key].length === 1) for (const k in f.grow) V[k] = clone(ORIG[k]);"
|
||||
in JS)
|
||||
check("the old settings page stays reachable (until P5)",
|
||||
'href="config" id="set-classic"' in web.page())
|
||||
|
||||
@@ -343,6 +360,11 @@ async def _refuse(s, root, sup):
|
||||
out["floor"] = await validate(peak_soc_floor=5)
|
||||
out["derive"] = await validate(ui__contract="dynamic", price_provider="")
|
||||
model = copy.deepcopy(base)
|
||||
model.update({"ui.contract": "dynamic", "price_provider": ""})
|
||||
out["derive_save"] = await _post(s, root + "config/save", {
|
||||
"model": model, "fingerprint": m["fingerprint"]})
|
||||
out["derive_writes"] = len(sup.writes)
|
||||
model = copy.deepcopy(base)
|
||||
model["inverters"][1]["setpoint_entity"] = ""
|
||||
out["save"] = await _post(s, root + "config/save",
|
||||
{"model": model, "fingerprint": m["fingerprint"]})
|
||||
@@ -374,6 +396,9 @@ check("a model no blob can say is refused beside its field, in plain words",
|
||||
_r["derive"]["messages"] == [{
|
||||
"field": "price_provider", "card": None, "tier": "bad",
|
||||
"text": W["derive"]["no_source"]["description"]}])
|
||||
check("a save no blob can say answers 422 with its sentence and writes nothing",
|
||||
_r["derive_save"][0] == 422 and _r["derive_writes"] == 0
|
||||
and _r["derive_save"][1]["messages"] == _r["derive"]["messages"])
|
||||
check("a refused save answers 422 with the same placement and writes nothing",
|
||||
_r["save"][0] == 422 and ("inverters.setpoint_entity", 1)
|
||||
in _at(_r["save"][1]) and _sup.writes == [])
|
||||
|
||||
Reference in New Issue
Block a user