core: add 'Rebuild will on wallet close' setting to skip the build wizard at close (REBUILD_ON_CLOSE); settings checkbox + tests
This commit is contained in:
@@ -251,6 +251,14 @@ class BalPlugin(BasePlugin):
|
||||
# (handled by BalWindow.get_wallet_password). Default ON.
|
||||
self.AUTO_SIGN = BalConfig(config, "bal_auto_sign", True)
|
||||
|
||||
# REBUILD_ON_CLOSE: when enabled (default), closing the wallet or
|
||||
# quitting Electrum runs the "Build your will" wizard
|
||||
# (BalBuildWillDialog) to rebuild and re-validate the will. When
|
||||
# disabled, on_close() only persists the current in-memory willitems to
|
||||
# the wallet DB: no rebuild dialog, no auto-sign/broadcast, no
|
||||
# invalidation prompts at close. Default ON.
|
||||
self.REBUILD_ON_CLOSE = BalConfig(config, "bal_rebuild_on_close", True)
|
||||
|
||||
# EDITABLE_DATES (Group C / C2): when enabled, the delivery-time and
|
||||
# check-alive date fields are editable everywhere (toolbar / Heirs tab),
|
||||
# not only inside the "Build your will" wizard. Default OFF, so the dates
|
||||
|
||||
@@ -456,6 +456,14 @@ class Plugin(BalPlugin):
|
||||
# be saved on a USB stick and a copy given to the heirs).
|
||||
heir_no_willexecutor = BalCheckBox(self.NO_WILLEXECUTOR)
|
||||
|
||||
# "Rebuild will on wallet close" checkbox. Bound to the persisted
|
||||
# REBUILD_ON_CLOSE config (default ON). When ticked, closing the wallet
|
||||
# / quitting Electrum runs the "Build your will" wizard to rebuild and
|
||||
# re-validate the will. When unticked, the will is only rebuilt when
|
||||
# the user presses Check/Prepare. Visible to all users (BASIC and
|
||||
# ADVANCED).
|
||||
heir_rebuild_on_close = BalCheckBox(self.REBUILD_ON_CLOSE)
|
||||
|
||||
# USER TYPE selector (SIMPLE / ADVANCED, global). A two-choice combo
|
||||
# (not a free-text field) bound to the USER_TYPE config:
|
||||
# index 0 -> "BASIC" -> stored value "basic" (DEFAULT)
|
||||
@@ -810,6 +818,24 @@ class Plugin(BalPlugin):
|
||||
2,
|
||||
)
|
||||
|
||||
# "Rebuild will on wallet close" row (always visible, BASIC + ADVANCED).
|
||||
# Placed below the rebroadcast button so the existing rows keep their
|
||||
# numbers.
|
||||
lbl_rebuild_on_close = QLabel(_("Rebuild will on wallet close"))
|
||||
help_rebuild_on_close = HelpButton(
|
||||
"Run the 'Build your will' wizard every time the wallet is closed "
|
||||
"or Electrum is quit, so the will is rebuilt and re-validated.\n"
|
||||
"When disabled, the will is only rebuilt when you press Check or "
|
||||
"Prepare. The last built state is still saved to the wallet."
|
||||
)
|
||||
grid.addWidget(lbl_rebuild_on_close, 15, 0)
|
||||
grid.addWidget(heir_rebuild_on_close, 15, 1)
|
||||
grid.addWidget(help_rebuild_on_close, 15, 2)
|
||||
reset_btn_rebuild_on_close = _make_reset_btn(
|
||||
self.REBUILD_ON_CLOSE, heir_rebuild_on_close, "check"
|
||||
)
|
||||
grid.addWidget(reset_btn_rebuild_on_close, 15, 3)
|
||||
|
||||
# ----------------------------------------------------------------- #
|
||||
# Group C / C4b: "Reset" button that restores the dialog settings to #
|
||||
# their factory defaults. It only resets the settings exposed by THIS #
|
||||
@@ -842,6 +868,7 @@ class Plugin(BalPlugin):
|
||||
(self.CALENDAR_APP, edit_calendar_app, "line"),
|
||||
(self.SAVE_HISTORY, heir_save_history, "check"),
|
||||
(self.HISTORY_LABEL, edit_history_label, "line"),
|
||||
(self.REBUILD_ON_CLOSE, heir_rebuild_on_close, "check"),
|
||||
]
|
||||
for cfg, widget, kind in resets:
|
||||
# Persist the default value back into the Electrum config.
|
||||
|
||||
@@ -1044,9 +1044,12 @@ class BalWindow:
|
||||
return
|
||||
|
||||
# 1) Business logic: build/save the will on close (unchanged behaviour).
|
||||
# REBUILD_ON_CLOSE gates the "Build your will" wizard only: the will is
|
||||
# still persisted so a manual Build/Check from the session is not lost.
|
||||
try:
|
||||
close_window = BalBuildWillDialog(self)
|
||||
close_window.build_will_task()
|
||||
if self.bal_plugin.REBUILD_ON_CLOSE.get():
|
||||
close_window = BalBuildWillDialog(self)
|
||||
close_window.build_will_task()
|
||||
self.save_willitems()
|
||||
except Exception as e:
|
||||
_logger.error(f"on_close: build/save will failed: {e}")
|
||||
|
||||
165
tests/test_rebuild_on_close_setting.py
Normal file
165
tests/test_rebuild_on_close_setting.py
Normal file
@@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Tests for the "Rebuild will on wallet close" (REBUILD_ON_CLOSE) setting.
|
||||
|
||||
Covers:
|
||||
|
||||
* the persisted ``bal_rebuild_on_close`` configuration key exists and
|
||||
defaults to ON (True), and can be turned off and read back;
|
||||
* ``BalWindow.on_close()`` runs the "Build your will" wizard
|
||||
(``BalBuildWillDialog.build_will_task()``) when the setting is ON;
|
||||
* ``BalWindow.on_close()`` SKIPS the wizard when the setting is OFF, but
|
||||
still calls ``save_willitems()`` so the last built state is persisted.
|
||||
|
||||
The on_close tests drive the real ``BalWindow.on_close`` method with a
|
||||
light-weight fake controller and a recording stub for ``BalBuildWillDialog``,
|
||||
so no full wallet/GUI machinery is needed.
|
||||
|
||||
Run:
|
||||
source /home/steal/devel/bal/electrum/env/bin/activate
|
||||
QT_QPA_PLATFORM=offscreen python3 tests/test_rebuild_on_close_setting.py
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import types
|
||||
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import bal.gui.qt.window as window_mod # noqa: E402
|
||||
from bal.core.plugin_base import BalConfig # noqa: E402
|
||||
|
||||
CONFIG_KEY = "bal_rebuild_on_close"
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Mocks
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
class FakeConfig:
|
||||
"""Minimal mock for Electrum's config object (key/value store)."""
|
||||
|
||||
def __init__(self):
|
||||
self._store = {}
|
||||
|
||||
def get(self, key, default=None):
|
||||
return self._store.get(key, default)
|
||||
|
||||
def set_key(self, key, value, save=True):
|
||||
self._store[key] = value
|
||||
|
||||
|
||||
class FakeBuildWillDialog:
|
||||
"""Recording stub for BalBuildWillDialog, patched into window.py."""
|
||||
|
||||
instances = []
|
||||
|
||||
def __init__(self, bal_window):
|
||||
self.bal_window = bal_window
|
||||
FakeBuildWillDialog.instances.append(self)
|
||||
|
||||
def build_will_task(self):
|
||||
self.bal_window._wizard_ran = True
|
||||
|
||||
|
||||
class _Tabs:
|
||||
def update(self):
|
||||
pass
|
||||
|
||||
|
||||
class _NoOp:
|
||||
willexecutors_action = None
|
||||
tabs = _Tabs()
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
def toggle_tab(self, tab):
|
||||
pass
|
||||
|
||||
def update(self):
|
||||
pass
|
||||
|
||||
def removeAction(self, action):
|
||||
pass
|
||||
|
||||
|
||||
def _make_fake_window(rebuild_on_close):
|
||||
"""Return a fake controller with the attributes on_close() touches."""
|
||||
fake = types.SimpleNamespace()
|
||||
fake.disable_plugin = False
|
||||
fake.bal_plugin = types.SimpleNamespace(
|
||||
REBUILD_ON_CLOSE=BalConfig(FakeConfig(), CONFIG_KEY, rebuild_on_close)
|
||||
)
|
||||
fake.willitems = {}
|
||||
fake.will = {}
|
||||
fake.saved = []
|
||||
fake.save_willitems = lambda: fake.saved.append("save")
|
||||
fake.heirs_tab = _NoOp()
|
||||
fake.will_tab = _NoOp()
|
||||
fake.tools_menu = _NoOp()
|
||||
fake.window = _NoOp()
|
||||
fake._menubar_initialized = True
|
||||
return fake
|
||||
|
||||
|
||||
def _call_on_close(fake):
|
||||
original = window_mod.BalBuildWillDialog
|
||||
FakeBuildWillDialog.instances = []
|
||||
try:
|
||||
window_mod.BalBuildWillDialog = FakeBuildWillDialog
|
||||
window_mod.BalWindow.on_close(fake)
|
||||
finally:
|
||||
window_mod.BalBuildWillDialog = original
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Config key
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
def test_rebuild_on_close_config_defaults_on():
|
||||
"""bal_rebuild_on_close must default to ON (True) when not yet stored."""
|
||||
cfg = FakeConfig()
|
||||
rebuild = BalConfig(cfg, CONFIG_KEY, True)
|
||||
assert rebuild.get() is True
|
||||
|
||||
|
||||
def test_rebuild_on_close_config_can_be_disabled():
|
||||
"""Once turned off and persisted, bal_rebuild_on_close reads back False."""
|
||||
cfg = FakeConfig()
|
||||
rebuild = BalConfig(cfg, CONFIG_KEY, True)
|
||||
rebuild.set(False)
|
||||
assert rebuild.get() is False
|
||||
# A fresh wrapper over the same config still sees the stored value.
|
||||
assert BalConfig(cfg, CONFIG_KEY, True).get() is False
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# on_close() behaviour
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
def test_on_close_runs_wizard_when_enabled():
|
||||
"""With REBUILD_ON_CLOSE ON, on_close() builds the will and saves it."""
|
||||
fake = _make_fake_window(True)
|
||||
_call_on_close(fake)
|
||||
assert len(FakeBuildWillDialog.instances) == 1, "wizard must be opened"
|
||||
assert fake._wizard_ran is True, "wizard build_will_task must run"
|
||||
assert fake.saved == ["save"], "save_willitems must run"
|
||||
|
||||
|
||||
def test_on_close_skips_wizard_when_disabled():
|
||||
"""With REBUILD_ON_CLOSE OFF, on_close() skips the wizard but saves."""
|
||||
fake = _make_fake_window(False)
|
||||
_call_on_close(fake)
|
||||
assert len(FakeBuildWillDialog.instances) == 0, "wizard must NOT be opened"
|
||||
assert not hasattr(fake, "_wizard_ran"), "wizard must not run"
|
||||
assert fake.saved == ["save"], "save_willitems must still run"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_rebuild_on_close_config_defaults_on()
|
||||
test_rebuild_on_close_config_can_be_disabled()
|
||||
test_on_close_runs_wizard_when_enabled()
|
||||
test_on_close_skips_wizard_when_disabled()
|
||||
print("OK: all tests passed")
|
||||
Reference in New Issue
Block a user