"""Ввод с клавиатуры и поведение таблиц. Отдельный файл, потому что проверять надо именно нажатия клавиш через QTest. Проверки уровня validate() однажды уже прошли зелёными, пока набрать цену в поле было физически невозможно: у поля суффикс « ₽», и «очистка» текста на каждом нажатии его ломала, из-за чего принимался только первый символ. """ import os from decimal import Decimal import pytest os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") from PySide6.QtTest import QTest # noqa: E402 from PySide6.QtWidgets import QApplication # noqa: E402 from app import journal # noqa: E402 from app.ui import theme # noqa: E402 from app.ui import widgets as w # noqa: E402 @pytest.fixture(scope="session") def qapp(): app = QApplication.instance() or QApplication([]) theme.apply(app) return app def type_in(spin, keys: str): """Набрать значение так, как это делает человек: выделить и напечатать.""" spin.show() spin.setFocus() spin.lineEdit().selectAll() QTest.keyClicks(spin.lineEdit(), keys) spin.interpretText() return spin.value_decimal() # --- деньги --------------------------------------------------------------- @pytest.mark.parametrize( "keys,expected", [ ("25", "25.00"), ("25,50", "25.50"), ("25.50", "25.50"), ("1200", "1200.00"), ("1234,56", "1234.56"), ("0,5", "0.50"), ("0.5", "0.50"), ], ) def test_price_can_actually_be_typed(qapp, keys, expected): """Каждый символ должен доходить до значения, а не только первый.""" spin = w.MoneySpin() spin.set_decimal(20) assert type_in(spin, keys) == Decimal(expected) spin.deleteLater() def test_currency_suffix_survives_typing(qapp): spin = w.MoneySpin() spin.set_decimal(20) type_in(spin, "137,40") assert spin.lineEdit().text().endswith("₽") assert spin.value_decimal() == Decimal("137.40") spin.deleteLater() def test_price_without_currency_also_works(qapp): spin = w.MoneySpin(currency="") spin.set_decimal(0) assert type_in(spin, "99.99") == Decimal("99.99") spin.deleteLater() # --- количества ----------------------------------------------------------- @pytest.mark.parametrize( "keys,expected", [("3", "3.000"), ("0,5", "0.500"), ("0.5", "0.500"), ("1,125", "1.125"), ("1200", "1200.000")], ) def test_quantity_can_actually_be_typed(qapp, keys, expected): spin = w.QtySpin() spin.set_decimal(1) assert type_in(spin, keys) == Decimal(expected) spin.deleteLater() def test_typing_over_an_existing_value_replaces_it(qapp): """Выделил всё, напечатал новое — старое не должно примешиваться.""" spin = w.MoneySpin() spin.set_decimal(1999) assert type_in(spin, "7") == Decimal("7.00") spin.deleteLater() # --- выделение в таблицах ------------------------------------------------- def rows_for(names): return [[w.text_item(name)] for name in names] def test_selection_follows_the_object_not_the_row(qapp): """Таблицы отсортированы: под тем же номером после перерисовки может оказаться другой объект, и «Удалить» сработает не на том.""" table = w.table(["Название"]) w.fill(table, rows_for(["Булка", "Ватрушка"]), keys=["b", "v"]) w.select_by_key(table, "v") assert w.selected_key(table) == "v" # Появился товар, встающий при сортировке первым. w.fill(table, rows_for(["Ананас", "Булка", "Ватрушка"]), keys=["a", "b", "v"]) assert w.selected_key(table) == "v" table.deleteLater() def test_selection_survives_a_plain_refresh(qapp): table = w.table(["Название"]) w.fill(table, rows_for(["Булка", "Ватрушка"]), keys=["b", "v"]) w.select_by_key(table, "b") w.fill(table, rows_for(["Булка", "Ватрушка"]), keys=["b", "v"]) assert w.selected_key(table) == "b" table.deleteLater() def test_selection_is_dropped_when_the_object_is_gone(qapp): table = w.table(["Название"]) w.fill(table, rows_for(["Булка", "Ватрушка"]), keys=["b", "v"]) w.select_by_key(table, "v") w.fill(table, rows_for(["Булка"]), keys=["b"]) assert w.selected_key(table) != "v" table.deleteLater() # --- правка цены целиком, как это делает пользователь --------------------- def test_price_edit_reaches_the_product(qapp, vault, buns): """Сквозной путь: набрать цены в диалоге и увидеть их в товаре.""" from app.ui.page_products import PriceDialog product = buns["мак"] dialog = PriceDialog("₽", product) assert type_in(dialog.cost, "25,50") == Decimal("25.50") assert type_in(dialog.retail, "44.90") == Decimal("44.90") journal.change_prices( vault, product.id, dialog.cost.value_decimal(), dialog.retail.value_decimal() ) updated = vault.doc.product(product.id) assert updated.cost_price == Decimal("25.50") assert updated.retail_price == Decimal("44.90") assert len(updated.price_history) == 2 dialog.deleteLater()