Chrome options with detach and the chrome_profile folder, Selenium adapter with explicit waits, login use case, credentials from environment variables, command line, unit tests, container tests against local fixtures and the step-1 screenshot. Task: MIL-003#1 Task: MIL-003#2 Task: MIL-003#3 Task: MIL-003#4
125 lines
3.7 KiB
Python
125 lines
3.7 KiB
Python
"""@file test_cli.py
|
|
@brief The command line accepts its options and the entry point wires the use case.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from tests.unit.fakes import FakeGym
|
|
|
|
from snack_and_lift import constants
|
|
from snack_and_lift.cli import main, parse_args
|
|
from snack_and_lift.infrastructure.browser import default_profile_dir
|
|
|
|
DUMMY_EMAIL = "dummy@example.invalid"
|
|
DUMMY_PASSWORD = "dummy-password"
|
|
|
|
|
|
@pytest.fixture
|
|
def credentials_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv(constants.ACCOUNT_EMAIL_ENV, DUMMY_EMAIL)
|
|
monkeypatch.setenv(constants.ACCOUNT_PASSWORD_ENV, DUMMY_PASSWORD)
|
|
|
|
|
|
def test_headless_is_off_by_default() -> None:
|
|
assert parse_args([]).headless is False
|
|
|
|
|
|
def test_headless_flag_is_read() -> None:
|
|
assert parse_args(["--headless"]).headless is True
|
|
|
|
|
|
def test_browser_stays_open_by_default_when_not_headless() -> None:
|
|
assert parse_args([]).keep_open is True
|
|
|
|
|
|
def test_browser_is_closed_by_default_when_headless() -> None:
|
|
assert parse_args(["--headless"]).keep_open is False
|
|
|
|
|
|
def test_keep_open_flag_overrides_headless_default() -> None:
|
|
assert parse_args(["--headless", "--keep-open"]).keep_open is True
|
|
|
|
|
|
def test_profile_dir_defaults_to_working_directory() -> None:
|
|
assert parse_args([]).profile_dir == default_profile_dir()
|
|
|
|
|
|
def test_profile_dir_option_is_read(tmp_path: Path) -> None:
|
|
assert parse_args(["--profile-dir", str(tmp_path)]).profile_dir == tmp_path
|
|
|
|
|
|
def test_screenshot_dir_is_optional() -> None:
|
|
assert parse_args([]).screenshot_dir is None
|
|
|
|
|
|
@pytest.mark.usefixtures("credentials_env")
|
|
def test_main_saves_both_screenshots_on_success(tmp_path: Path) -> None:
|
|
gym = FakeGym()
|
|
|
|
code = main(["--screenshot-dir", str(tmp_path)], lambda *_: gym)
|
|
|
|
assert code == 0
|
|
assert gym.logins == [(DUMMY_EMAIL, DUMMY_PASSWORD)]
|
|
assert gym.screenshots == [tmp_path / "step-1.png", tmp_path / "step-2.png"]
|
|
assert gym.calls == ["open_site", "save_screenshot", "log_in", "save_screenshot"]
|
|
|
|
|
|
@pytest.mark.usefixtures("credentials_env")
|
|
def test_main_saves_no_screenshot_without_directory() -> None:
|
|
gym = FakeGym()
|
|
|
|
assert main([], lambda *_: gym) == 0
|
|
assert gym.screenshots == []
|
|
|
|
|
|
def test_main_returns_2_when_credentials_are_missing(
|
|
monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
monkeypatch.delenv(constants.ACCOUNT_EMAIL_ENV, raising=False)
|
|
monkeypatch.delenv(constants.ACCOUNT_PASSWORD_ENV, raising=False)
|
|
gym = FakeGym()
|
|
|
|
code = main([], lambda *_: gym)
|
|
|
|
assert code == 2
|
|
assert gym.calls == []
|
|
assert constants.ACCOUNT_EMAIL_ENV in caplog.text
|
|
|
|
|
|
@pytest.mark.usefixtures("credentials_env")
|
|
def test_main_returns_1_when_login_fails() -> None:
|
|
gym = FakeGym(login_error="Invalid credentials")
|
|
|
|
assert main(["--headless"], lambda *_: gym) == 1
|
|
assert gym.closed is True
|
|
|
|
|
|
@pytest.mark.usefixtures("credentials_env")
|
|
def test_main_closes_the_gym_when_not_kept_open() -> None:
|
|
gym = FakeGym()
|
|
|
|
assert main(["--headless"], lambda *_: gym) == 0
|
|
assert gym.closed is True
|
|
|
|
|
|
@pytest.mark.usefixtures("credentials_env")
|
|
def test_main_keeps_the_gym_open_when_asked() -> None:
|
|
gym = FakeGym()
|
|
|
|
assert main([], lambda *_: gym) == 0
|
|
assert gym.closed is False
|
|
|
|
|
|
@pytest.mark.usefixtures("credentials_env")
|
|
def test_main_passes_options_to_the_factory(tmp_path: Path) -> None:
|
|
received: list[tuple[bool, Path, bool]] = []
|
|
|
|
def factory(headless: bool, profile_dir: Path, keep_open: bool) -> FakeGym:
|
|
received.append((headless, profile_dir, keep_open))
|
|
return FakeGym()
|
|
|
|
main(["--headless", "--profile-dir", str(tmp_path)], factory)
|
|
|
|
assert received == [(True, tmp_path, False)]
|