From af9cae85dea4f4dbac6dcab481e4a44d2aae8562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80?= Date: Sun, 27 Sep 2026 16:20:58 +0900 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA?= =?UTF-8?q?=D0=B0=20=D1=84=D0=B0=D0=B9=D0=BB=D0=BE=D0=B2=20=D1=81=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=BF=D1=8C=D1=8E=D1=82=D0=B5=D1=80=D0=B0=20=D1=81?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D0=B0=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0?= =?UTF-8?q?=D0=B5=D1=82:=20=D1=84=D0=BE=D1=80=D0=BC=D0=B0=20=D1=81=D0=BE?= =?UTF-8?q?=D0=B1=D0=B8=D1=80=D0=B0=D0=BB=D0=B0=D1=81=D1=8C=20=D1=83=D0=B6?= =?UTF-8?q?=D0=B5=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D0=B1=D0=BB=D0=BE?= =?UTF-8?q?=D0=BA=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B8=20=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D1=8F=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B0,=20=D0=B8=20=D0=B1?= =?UTF-8?q?=D1=80=D0=B0=D1=83=D0=B7=D0=B5=D1=80=20=D0=BE=D1=82=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D1=8F=D0=BB=20=D0=BF=D1=83=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D0=B9=20=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81=20(=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=B1=D0=BA=D0=B0=20422)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_app.py | 10 ++++++++++ web/static/panel.js | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/test_app.py b/tests/test_app.py index 95f4139..480691b 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -1,3 +1,4 @@ +from pathlib import Path from urllib.parse import unquote import pytest @@ -186,3 +187,12 @@ def test_live_status(logged_in): data = logged_in.get("/live/status").json() assert data["ok"] is True assert {vm["name"] for vm in data["vms"]} == {"web01", "db01"} + + +def test_uploader_collects_file_before_disabling_input(): + """Заблокированное поле браузер не отправляет: загрузка уходила пустой (44 байта) и получала 422.""" + script = (Path(__file__).resolve().parents[1] / "web" / "static" / "panel.js").read_text() + uploader = script[script.index("function wireUploader"):script.index("/* ---------------------------------------------------- operation page */")] + assert "input.disabled = active" in uploader + assert uploader.index("new FormData(form)") < uploader.index("setActive(true)") + assert "xhr.send(data)" in uploader diff --git a/web/static/panel.js b/web/static/panel.js index 60a41b1..049c878 100644 --- a/web/static/panel.js +++ b/web/static/panel.js @@ -300,6 +300,8 @@ const file = input.files[0]; if (!valid(file)) { setError('Выберите файл'); return; } const started = Date.now(); + // FormData собираем до setActive: заблокированное поле файла браузер в отправку не включает. + const data = new FormData(form); xhr = new XMLHttpRequest(); setActive(true); progress.hidden = false; progress.classList.remove('success', 'danger'); statusEl.textContent = 'Загрузка на сервер…'; @@ -337,7 +339,7 @@ xhr.open('POST', form.action); xhr.setRequestHeader('Accept', 'application/json'); xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); - xhr.send(new FormData(form)); + xhr.send(data); }); showFile(); }