fix: add btnClearUpdateLock markup for PR #5688 v2 frontend path
Address Greptile P1 review (discussion r3530941293) on commit e8f7e28a:
the v2 frontend recovery path was dead at the DOM level. static/ui.js
references $('btnClearUpdateLock') but no matching element existed in
static/index.html, so users hitting a lock conflict never saw a clickable
affordance -- the error panel was the only visible state.
Add the missing button next to btnForceUpdate in the update-banner action
row:
<button class="update-btn" id="btnClearUpdateLock"
style="display:none"
onclick="applyClearUpdateLock(this)">
Clear lock and retry update
</button>
Style mirrors the neutral update-btn (NOT the red --error used by the
destructive Force update) so the recovery affordance doesn't read as a
warning. Hidden by default; revealed by the existing _showUpdateError()
helper when res.lock_conflict is set.
Regression tests in TestClearLockButton (3 tests):
- test_clear_lock_button_exists
- test_clear_lock_button_hidden_by_default
- test_clear_lock_button_calls_applyClearUpdateLock_handler
These lock in the contract that the button element must be present and
correctly bound; future refactors that rename the id or strip the button
will fail loud. Mirrors the existing TestIndexHtmlBanner conventions
for btnForceUpdate.
Verification:
- ./scripts/test.sh tests/test_updates.py -k lock -> 35 passed
- ./scripts/test.sh tests/test_update_banner_fixes.py -> 102 passed
- ./scripts/test.sh tests/test_api_timeout.py -> 8 passed
- ruff_lint --diff upstream/master -> 0 new violations
This commit is contained in:
parent
e8f7e28a3c
commit
dcec232dfa
2 changed files with 34 additions and 0 deletions
|
|
@ -415,6 +415,7 @@
|
|||
<button class="update-btn" onclick="dismissUpdate()">Later</button>
|
||||
<button class="update-btn update-primary" id="btnApplyUpdate" onclick="applyUpdates()">Update Now</button>
|
||||
<button class="update-btn" id="btnForceUpdate" style="display:none;background:var(--error,#e05);color:#fff;border-color:var(--error,#e05)" onclick="forceUpdate(this)">Force update</button>
|
||||
<button class="update-btn" id="btnClearUpdateLock" style="display:none" onclick="applyClearUpdateLock(this)">Clear lock and retry update</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="update-banner" id="staleClientBanner">
|
||||
|
|
|
|||
|
|
@ -1805,6 +1805,39 @@ class TestIndexHtmlBanner:
|
|||
)
|
||||
|
||||
|
||||
class TestClearLockButton:
|
||||
"""PR #5688 follow-up: clear-lock button must exist in index.html and be
|
||||
hidden by default. Without this, the v2 frontend recovery path is dead --
|
||||
$('btnClearUpdateLock') returns null at runtime so the lock-only error
|
||||
branch in _showUpdateError() never reveals a clickable affordance (P1).
|
||||
"""
|
||||
|
||||
def test_clear_lock_button_exists(self):
|
||||
src = read('static/index.html')
|
||||
assert 'id="btnClearUpdateLock"' in src, (
|
||||
"index.html must have #btnClearUpdateLock button (hidden by "
|
||||
"default) -- PR #5688 v2 frontend path"
|
||||
)
|
||||
|
||||
def test_clear_lock_button_hidden_by_default(self):
|
||||
src = read('static/index.html')
|
||||
m = re.search(r'id="btnClearUpdateLock"[^>]*>', src)
|
||||
assert m, "#btnClearUpdateLock not found"
|
||||
tag = m.group(0)
|
||||
assert 'display:none' in tag, (
|
||||
"#btnClearUpdateLock must be hidden by default (display:none)"
|
||||
)
|
||||
|
||||
def test_clear_lock_button_calls_applyClearUpdateLock_handler(self):
|
||||
src = read('static/index.html')
|
||||
m = re.search(r'id="btnClearUpdateLock"[^>]*>', src)
|
||||
assert m, "#btnClearUpdateLock not found"
|
||||
tag = m.group(0)
|
||||
assert 'applyClearUpdateLock(this)' in tag, (
|
||||
"#btnClearUpdateLock onclick must invoke applyClearUpdateLock"
|
||||
)
|
||||
|
||||
|
||||
# ── Regression: sequential webui+agent update — restart coordination ──────────
|
||||
|
||||
class TestSequentialUpdateRestartCoordination:
|
||||
|
|
|
|||
Loading…
Reference in a new issue