feat: wire editor ops (delete/silence/reverse/trim) + undo/redo + save-as

This commit is contained in:
2026-07-02 18:22:39 +02:00
parent a11e4859dd
commit 4d54f29514
2 changed files with 177 additions and 9 deletions
+71
View File
@@ -658,3 +658,74 @@ def test_audio_editor_dialog_scaffold(win):
# a bad path decodes to an empty waveform without crashing
dlg._reload()
dlg.close()
def test_editor_delete_builds_command(win, tmp_path, monkeypatch):
import main as m, pytest
src = tmp_path / "v0.wav"; src.write_bytes(b"")
dlg = m.AudioEditorDialog(str(src), parent=win)
dlg._wave.set_view(0.0, 5.0); dlg._wave.set_selection(1.0, 3.0)
seen = {}
class _Stop(Exception): pass
def fake(inp, s, e, out):
seen.update(inp=inp, s=s, e=e); raise _Stop
monkeypatch.setattr(m, "build_audio_delete_command", fake)
with pytest.raises(_Stop):
dlg._on_delete()
assert seen["inp"] == str(src) and seen["s"] == 1.0 and seen["e"] == 3.0
def test_editor_op_no_region_is_safe(win, tmp_path, monkeypatch):
import main as m
src = tmp_path / "v0.wav"; src.write_bytes(b"")
dlg = m.AudioEditorDialog(str(src), parent=win)
dlg._wave.set_view(0.0, 5.0); dlg._wave.set_selection(2.0, 2.0) # zero-width
n = {"c": 0}
monkeypatch.setattr(m, "build_audio_delete_command",
lambda *a: n.__setitem__("c", n["c"] + 1) or [])
dlg._on_delete()
assert n["c"] == 0 # guarded, builder never called
def test_editor_delete_whole_clip_blocked(win, tmp_path, monkeypatch):
import main as m
src = tmp_path / "v0.wav"; src.write_bytes(b"")
dlg = m.AudioEditorDialog(str(src), parent=win)
dlg._clip_dur = 5.0
dlg._wave.set_view(0.0, 5.0); dlg._wave.set_selection(0.0, 5.0) # whole clip
n = {"c": 0}
monkeypatch.setattr(m, "build_audio_delete_command",
lambda *a: n.__setitem__("c", n["c"] + 1) or [])
dlg._on_delete()
assert n["c"] == 0 # can't delete the whole clip
def test_editor_op_uses_clip_duration_not_view(win, tmp_path, monkeypatch):
import main as m, pytest
src = tmp_path / "v0.wav"; src.write_bytes(b"")
dlg = m.AudioEditorDialog(str(src), parent=win)
dlg._clip_dur = 5.0
dlg._wave.set_view(0.5, 4.0) # zoomed sub-window (view ends at 4.5)
dlg._wave.set_selection(1.0, 4.5)
seen = {}
class _Stop(Exception): pass
def fake(inp, s, e, out):
seen.update(s=s, e=e); raise _Stop
monkeypatch.setattr(m, "build_audio_silence_command", fake)
with pytest.raises(_Stop):
dlg._on_silence()
assert seen["e"] == 4.5 # clip duration, not the 4.0 view span
def test_editor_undo_redo(win, tmp_path, monkeypatch):
import main as m
src = tmp_path / "v0.wav"; src.write_bytes(b"")
dlg = m.AudioEditorDialog(str(src), parent=win)
monkeypatch.setattr(dlg, "_reload", lambda: None)
dlg._versions = [str(src), "/t/v1.wav", "/t/v2.wav"]; dlg._ver_idx = 2
dlg._on_undo(); assert dlg._ver_idx == 1
dlg._on_undo(); assert dlg._ver_idx == 0
dlg._on_undo(); assert dlg._ver_idx == 0 # clamped at base
dlg._on_redo(); assert dlg._ver_idx == 1
dlg._on_redo(); assert dlg._ver_idx == 2
dlg._on_redo(); assert dlg._ver_idx == 2 # clamped at top