MIL-002: Step 1 - User interface
Review record RC-024 (Go, autonomous mode, not independent). Task: MIL-002#1 Task: MIL-002#2 Task: MIL-002#3 Task: MIL-002#4
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
"""End-to-end tests of the flash card window.
|
||||
|
||||
@file test_flash_card_window.py
|
||||
@brief Builds the real window with Tk and checks it against the wireframe.
|
||||
"""
|
||||
|
||||
import tkinter as tk
|
||||
from collections.abc import Iterator
|
||||
|
||||
import pytest
|
||||
|
||||
from flash_card.constants import (
|
||||
BACKGROUND_COLOR,
|
||||
CARD_HEIGHT,
|
||||
CARD_WIDTH,
|
||||
FRONT_TEXT_COLOR,
|
||||
LANGUAGE_FONT,
|
||||
PLACEHOLDER_LANGUAGE_NAME,
|
||||
PLACEHOLDER_WORD,
|
||||
WINDOW_PADDING,
|
||||
WINDOW_TITLE,
|
||||
WORD_FONT,
|
||||
)
|
||||
from flash_card.presentation.features.flash_card.flash_card_view import FlashCardView
|
||||
from flash_card.presentation.features.flash_card.flash_card_view_model import (
|
||||
FlashCardViewModel,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def window() -> Iterator[tuple[tk.Tk, FlashCardView]]:
|
||||
# One Tk per module: a second interpreter right after destroying the
|
||||
# first fails to find init.tcl on some Windows Python installs.
|
||||
try:
|
||||
root = tk.Tk()
|
||||
except tk.TclError:
|
||||
pytest.skip("no display available for Tk")
|
||||
view = FlashCardView(root, FlashCardViewModel())
|
||||
root.update_idletasks()
|
||||
yield root, view
|
||||
root.destroy()
|
||||
|
||||
|
||||
def test_window_has_title_background_and_padding(
|
||||
window: tuple[tk.Tk, FlashCardView],
|
||||
) -> None:
|
||||
root, _view = window
|
||||
assert root.title() == WINDOW_TITLE
|
||||
assert root.cget("bg") == BACKGROUND_COLOR
|
||||
assert int(str(root.cget("padx"))) == WINDOW_PADDING
|
||||
assert int(str(root.cget("pady"))) == WINDOW_PADDING
|
||||
|
||||
|
||||
def test_canvas_has_card_size_and_spans_two_columns(
|
||||
window: tuple[tk.Tk, FlashCardView],
|
||||
) -> None:
|
||||
_root, view = window
|
||||
assert int(str(view.canvas.cget("width"))) == CARD_WIDTH
|
||||
assert int(str(view.canvas.cget("height"))) == CARD_HEIGHT
|
||||
info = view.canvas.grid_info()
|
||||
assert (int(info["row"]), int(info["column"])) == (0, 0)
|
||||
assert int(info["columnspan"]) == 2
|
||||
|
||||
|
||||
def test_canvas_holds_one_image_and_two_texts(
|
||||
window: tuple[tk.Tk, FlashCardView],
|
||||
) -> None:
|
||||
_root, view = window
|
||||
types = [view.canvas.type(item) for item in view.canvas.find_all()]
|
||||
assert types.count("image") == 1
|
||||
assert types.count("text") == 2
|
||||
assert len(types) == 3
|
||||
|
||||
|
||||
def test_texts_show_placeholders_with_font_and_colour(
|
||||
window: tuple[tk.Tk, FlashCardView],
|
||||
) -> None:
|
||||
_root, view = window
|
||||
canvas = view.canvas
|
||||
expected = [
|
||||
(view.language_text_id, PLACEHOLDER_LANGUAGE_NAME, LANGUAGE_FONT),
|
||||
(view.word_text_id, PLACEHOLDER_WORD, WORD_FONT),
|
||||
]
|
||||
for item, text, font in expected:
|
||||
assert canvas.itemcget(item, "text") == text
|
||||
assert canvas.itemcget(item, "fill") == FRONT_TEXT_COLOR
|
||||
family, size, style = font
|
||||
assert canvas.itemcget(item, "font").split() == [family, str(size), style]
|
||||
|
||||
|
||||
def test_buttons_sit_in_row_one_with_their_images(
|
||||
window: tuple[tk.Tk, FlashCardView],
|
||||
) -> None:
|
||||
_root, view = window
|
||||
wrong = view.wrong_button.grid_info()
|
||||
right = view.right_button.grid_info()
|
||||
assert (int(wrong["row"]), int(wrong["column"])) == (1, 0)
|
||||
assert (int(right["row"]), int(right["column"])) == (1, 1)
|
||||
wrong_image = str(view.wrong_button.cget("image"))
|
||||
right_image = str(view.right_button.cget("image"))
|
||||
assert wrong_image
|
||||
assert right_image
|
||||
assert wrong_image != right_image
|
||||
assert not str(view.wrong_button.cget("command"))
|
||||
assert not str(view.right_button.cget("command"))
|
||||
+20
-4
@@ -1,11 +1,27 @@
|
||||
"""Tests of the entry point.
|
||||
|
||||
@file test_app.py
|
||||
@brief Checks main of flash_card.presentation.app.
|
||||
@brief Checks main of flash_card.presentation.app without opening a window.
|
||||
"""
|
||||
|
||||
from flash_card.presentation.app import main
|
||||
import pytest
|
||||
|
||||
from flash_card.presentation import app
|
||||
|
||||
|
||||
def test_main_returns_zero_without_arguments() -> None:
|
||||
assert main([]) == 0
|
||||
class _FakeRoot:
|
||||
def __init__(self) -> None:
|
||||
self.mainloop_calls = 0
|
||||
|
||||
def mainloop(self) -> None:
|
||||
self.mainloop_calls += 1
|
||||
|
||||
|
||||
def test_main_runs_main_loop_and_returns_zero(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
root = _FakeRoot()
|
||||
monkeypatch.setattr(app, "create_application", lambda: (root, object()))
|
||||
|
||||
assert app.main([]) == 0
|
||||
assert root.mainloop_calls == 1
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
"""Tests of the flash card view model.
|
||||
|
||||
@file test_flash_card_view_model.py
|
||||
@brief Checks the placeholder texts of FlashCardViewModel.
|
||||
"""
|
||||
|
||||
from flash_card.constants import PLACEHOLDER_LANGUAGE_NAME, PLACEHOLDER_WORD
|
||||
from flash_card.presentation.features.flash_card.flash_card_view_model import (
|
||||
FlashCardViewModel,
|
||||
)
|
||||
|
||||
|
||||
def test_language_name_returns_placeholder() -> None:
|
||||
assert FlashCardViewModel().language_name == PLACEHOLDER_LANGUAGE_NAME
|
||||
|
||||
|
||||
def test_text_returns_placeholder_word() -> None:
|
||||
assert FlashCardViewModel().text == PLACEHOLDER_WORD
|
||||
@@ -0,0 +1,58 @@
|
||||
"""Tests of the image generator tool.
|
||||
|
||||
@file test_generate_images.py
|
||||
@brief Checks the PNG files written by tools/generate_images.py.
|
||||
"""
|
||||
|
||||
import importlib.util
|
||||
import struct
|
||||
from pathlib import Path
|
||||
from types import ModuleType
|
||||
|
||||
import pytest
|
||||
|
||||
from flash_card.constants import (
|
||||
CARD_BACK_IMAGE_NAME,
|
||||
CARD_FRONT_IMAGE_NAME,
|
||||
CARD_HEIGHT,
|
||||
CARD_WIDTH,
|
||||
RIGHT_IMAGE_NAME,
|
||||
WRONG_IMAGE_NAME,
|
||||
)
|
||||
|
||||
_TOOL_PATH = Path(__file__).resolve().parents[2] / "tools" / "generate_images.py"
|
||||
_SIGNATURE = b"\x89PNG\r\n\x1a\n"
|
||||
|
||||
|
||||
def _load_tool() -> ModuleType:
|
||||
spec = importlib.util.spec_from_file_location("generate_images", _TOOL_PATH)
|
||||
assert spec is not None
|
||||
assert spec.loader is not None
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def generated(tmp_path_factory: pytest.TempPathFactory) -> Path:
|
||||
output_dir = tmp_path_factory.mktemp("assets")
|
||||
assert _load_tool().main(["--output-dir", str(output_dir)]) == 0
|
||||
return output_dir
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("name", "width", "height"),
|
||||
[
|
||||
(CARD_FRONT_IMAGE_NAME, CARD_WIDTH, CARD_HEIGHT),
|
||||
(CARD_BACK_IMAGE_NAME, CARD_WIDTH, CARD_HEIGHT),
|
||||
(RIGHT_IMAGE_NAME, 100, 100),
|
||||
(WRONG_IMAGE_NAME, 100, 100),
|
||||
],
|
||||
)
|
||||
def test_generate_images_writes_png_with_expected_size(
|
||||
generated: Path, name: str, width: int, height: int
|
||||
) -> None:
|
||||
data = (generated / name).read_bytes()
|
||||
assert data.startswith(_SIGNATURE)
|
||||
assert data[12:16] == b"IHDR"
|
||||
assert struct.unpack(">II", data[16:24]) == (width, height)
|
||||
Reference in New Issue
Block a user