feat(falyx): add persistent prompt history, multiline input support, and new enum tests
- Added `enable_prompt_history` & `prompt_history_base_dir` to Falyx, using FileHistory to persist inputs to `~/.{program}_history` - Added `multiline` option to UserInputAction and passed through to PromptSession - Updated examples (`argument_examples.py`, `confirm_example.py`) to enable prompt history and add TLDR examples - Improved CLI usage tips for clarity (`[COMMAND]` instead of `[KEY]`) - Added `test_action_types.py` and expanded `test_main.py` for init and parser coverage - Bumped version to 0.1.76
This commit is contained in:
@ -82,6 +82,7 @@ flx = Falyx(
|
|||||||
program="argument_examples.py",
|
program="argument_examples.py",
|
||||||
hide_menu_table=True,
|
hide_menu_table=True,
|
||||||
show_placeholder_menu=True,
|
show_placeholder_menu=True,
|
||||||
|
enable_prompt_history=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
flx.add_command(
|
flx.add_command(
|
||||||
|
@ -101,10 +101,16 @@ def dog_config(parser: CommandArgumentParser) -> None:
|
|||||||
lazy_resolver=False,
|
lazy_resolver=False,
|
||||||
help="List of dogs to process.",
|
help="List of dogs to process.",
|
||||||
)
|
)
|
||||||
|
parser.add_tldr_examples(
|
||||||
|
[
|
||||||
|
("max", "Process the dog named Max"),
|
||||||
|
("bella buddy max", "Process the dogs named Bella, Buddy, and Max"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
flx = Falyx("Save Dogs Example")
|
flx = Falyx("Save Dogs Example", program="confirm_example.py")
|
||||||
|
|
||||||
flx.add_command(
|
flx.add_command(
|
||||||
key="D",
|
key="D",
|
||||||
|
@ -63,6 +63,7 @@ class UserInputAction(BaseAction):
|
|||||||
*,
|
*,
|
||||||
prompt_message: str = "Input > ",
|
prompt_message: str = "Input > ",
|
||||||
default_text: str = "",
|
default_text: str = "",
|
||||||
|
multiline: bool = False,
|
||||||
validator: Validator | None = None,
|
validator: Validator | None = None,
|
||||||
prompt_session: PromptSession | None = None,
|
prompt_session: PromptSession | None = None,
|
||||||
inject_last_result: bool = False,
|
inject_last_result: bool = False,
|
||||||
@ -72,11 +73,12 @@ class UserInputAction(BaseAction):
|
|||||||
inject_last_result=inject_last_result,
|
inject_last_result=inject_last_result,
|
||||||
)
|
)
|
||||||
self.prompt_message = prompt_message
|
self.prompt_message = prompt_message
|
||||||
|
self.default_text = default_text
|
||||||
|
self.multiline = multiline
|
||||||
self.validator = validator
|
self.validator = validator
|
||||||
self.prompt_session = prompt_session or PromptSession(
|
self.prompt_session = prompt_session or PromptSession(
|
||||||
interrupt_exception=CancelSignal
|
interrupt_exception=CancelSignal
|
||||||
)
|
)
|
||||||
self.default_text = default_text
|
|
||||||
|
|
||||||
def get_infer_target(self) -> tuple[None, None]:
|
def get_infer_target(self) -> tuple[None, None]:
|
||||||
return None, None
|
return None, None
|
||||||
@ -100,6 +102,7 @@ class UserInputAction(BaseAction):
|
|||||||
rich_text_to_prompt_text(prompt_message),
|
rich_text_to_prompt_text(prompt_message),
|
||||||
validator=self.validator,
|
validator=self.validator,
|
||||||
default=kwargs.get("default_text", self.default_text),
|
default=kwargs.get("default_text", self.default_text),
|
||||||
|
multiline=self.multiline,
|
||||||
)
|
)
|
||||||
context.result = answer
|
context.result = answer
|
||||||
await self.hooks.trigger(HookType.ON_SUCCESS, context)
|
await self.hooks.trigger(HookType.ON_SUCCESS, context)
|
||||||
|
@ -27,11 +27,13 @@ import sys
|
|||||||
from argparse import ArgumentParser, Namespace, _SubParsersAction
|
from argparse import ArgumentParser, Namespace, _SubParsersAction
|
||||||
from difflib import get_close_matches
|
from difflib import get_close_matches
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
|
from pathlib import Path
|
||||||
from random import choice
|
from random import choice
|
||||||
from typing import Any, Callable
|
from typing import Any, Callable
|
||||||
|
|
||||||
from prompt_toolkit import PromptSession
|
from prompt_toolkit import PromptSession
|
||||||
from prompt_toolkit.formatted_text import StyleAndTextTuples
|
from prompt_toolkit.formatted_text import StyleAndTextTuples
|
||||||
|
from prompt_toolkit.history import FileHistory
|
||||||
from prompt_toolkit.key_binding import KeyBindings
|
from prompt_toolkit.key_binding import KeyBindings
|
||||||
from prompt_toolkit.patch_stdout import patch_stdout
|
from prompt_toolkit.patch_stdout import patch_stdout
|
||||||
from prompt_toolkit.validation import ValidationError
|
from prompt_toolkit.validation import ValidationError
|
||||||
@ -126,7 +128,6 @@ class Falyx:
|
|||||||
title: str | Markdown = "Menu",
|
title: str | Markdown = "Menu",
|
||||||
*,
|
*,
|
||||||
program: str | None = "falyx",
|
program: str | None = "falyx",
|
||||||
program_style: str = OneColors.WHITE,
|
|
||||||
usage: str | None = None,
|
usage: str | None = None,
|
||||||
description: str | None = "Falyx CLI - Run structured async command workflows.",
|
description: str | None = "Falyx CLI - Run structured async command workflows.",
|
||||||
epilog: str | None = None,
|
epilog: str | None = None,
|
||||||
@ -148,10 +149,12 @@ class Falyx:
|
|||||||
custom_table: Callable[[Falyx], Table] | Table | None = None,
|
custom_table: Callable[[Falyx], Table] | Table | None = None,
|
||||||
hide_menu_table: bool = False,
|
hide_menu_table: bool = False,
|
||||||
show_placeholder_menu: bool = False,
|
show_placeholder_menu: bool = False,
|
||||||
|
prompt_history_base_dir: Path = Path.home(),
|
||||||
|
enable_prompt_history: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initializes the Falyx object."""
|
"""Initializes the Falyx object."""
|
||||||
self.title: str | Markdown = title
|
self.title: str | Markdown = title
|
||||||
self.program: str | None = program
|
self.program: str = program or ""
|
||||||
self.usage: str | None = usage
|
self.usage: str | None = usage
|
||||||
self.description: str | None = description
|
self.description: str | None = description
|
||||||
self.epilog: str | None = epilog
|
self.epilog: str | None = epilog
|
||||||
@ -184,6 +187,14 @@ class Falyx:
|
|||||||
self.help_command: Command | None = (
|
self.help_command: Command | None = (
|
||||||
self._get_help_command() if include_help_command else None
|
self._get_help_command() if include_help_command else None
|
||||||
)
|
)
|
||||||
|
if enable_prompt_history:
|
||||||
|
program = (self.program or "falyx").split(".")[0].replace(" ", "_")
|
||||||
|
self.history_path: Path = (
|
||||||
|
Path(prompt_history_base_dir) / f".{program}_history"
|
||||||
|
)
|
||||||
|
self.history: FileHistory | None = FileHistory(self.history_path)
|
||||||
|
else:
|
||||||
|
self.history = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_cli_mode(self) -> bool:
|
def is_cli_mode(self) -> bool:
|
||||||
@ -334,30 +345,31 @@ class Falyx:
|
|||||||
def get_tip(self) -> str:
|
def get_tip(self) -> str:
|
||||||
program = f"{self.program} run " if self.is_cli_mode else ""
|
program = f"{self.program} run " if self.is_cli_mode else ""
|
||||||
tips = [
|
tips = [
|
||||||
f"Tip: Use '{program}?[KEY]' to preview a command.",
|
f"Tip: Use '{program}?[COMMAND]' to preview a command.",
|
||||||
f"Tip: Use '{program}?' alone to list all commands at any time.",
|
|
||||||
"Tip: Every command supports aliases—try abbreviating the name!",
|
"Tip: Every command supports aliases—try abbreviating the name!",
|
||||||
f"Tip: Use '{program}H' to reopen this help menu anytime.",
|
f"Tip: Use '{program}H' to reopen this help menu anytime.",
|
||||||
f"Tip: '{program}[KEY] --help' prints a detailed help message.",
|
f"Tip: '{program}[COMMAND] --help' prints a detailed help message.",
|
||||||
"Tip: Mix CLI and menu mode—commands run the same way in both.",
|
"Tip: [bold]CLI[/] and [bold]Menu[/] mode—commands run the same way in both.",
|
||||||
f"Tip: Use '{self.program} --never-prompt' to disable all prompts for the [bold italic]entire menu session[/].",
|
f"Tip: Use '{self.program} --never-prompt' to disable all prompts for the [bold italic]entire menu session[/].",
|
||||||
f"Tip: Use '{self.program} --verbose' to enable debug logging for a menu session.",
|
f"Tip: Use '{self.program} --verbose' to enable debug logging for a menu session.",
|
||||||
f"Tip: '{self.program} --debug-hooks' will trace every before/after hook in action.",
|
f"Tip: '{self.program} --debug-hooks' will trace every before/after hook in action.",
|
||||||
f"Tip: Run commands directly from the CLI: '{self.program} run [KEY] [OPTIONS]'.",
|
f"Tip: Run commands directly from the CLI: '{self.program} run [COMMAND] [OPTIONS]'.",
|
||||||
]
|
]
|
||||||
if self.is_cli_mode:
|
if self.is_cli_mode:
|
||||||
tips.extend(
|
tips.extend(
|
||||||
[
|
[
|
||||||
f"Tip: Use '{self.program} --never-prompt run [KEY]' to disable all prompts for [bold italic]just this command[/].",
|
f"Tip: Use '{self.program} run ?' to list all commands at any time.",
|
||||||
f"Tip: Use '{self.program} run --skip-confirm [KEY]' to skip confirmations.",
|
f"Tip: Use '{self.program} --never-prompt run [COMMAND] [OPTIONS]' to disable all prompts for [bold italic]just this command[/].",
|
||||||
f"Tip: Use '{self.program} run --summary [KEY]' to print a post-run summary.",
|
f"Tip: Use '{self.program} run --skip-confirm [COMMAND] [OPTIONS]' to skip confirmations.",
|
||||||
f"Tip: Use '{self.program} --verbose run [KEY]' to enable debug logging for any run.",
|
f"Tip: Use '{self.program} run --summary [COMMAND] [OPTIONS]' to print a post-run summary.",
|
||||||
|
f"Tip: Use '{self.program} --verbose run [COMMAND] [OPTIONS]' to enable debug logging for any run.",
|
||||||
"Tip: Use '--skip-confirm' for automation scripts where no prompts are wanted.",
|
"Tip: Use '--skip-confirm' for automation scripts where no prompts are wanted.",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
tips.extend(
|
tips.extend(
|
||||||
[
|
[
|
||||||
|
"Tip: Use '[?]' alone to list all commands at any time.",
|
||||||
"Tip: '[CTRL+KEY]' toggles are available in menu mode for quick switches.",
|
"Tip: '[CTRL+KEY]' toggles are available in menu mode for quick switches.",
|
||||||
"Tip: '[Y]' opens the command history viewer.",
|
"Tip: '[Y]' opens the command history viewer.",
|
||||||
"Tip: Use '[X]' in menu mode to exit.",
|
"Tip: Use '[X]' in menu mode to exit.",
|
||||||
@ -514,6 +526,7 @@ class Falyx:
|
|||||||
placeholder = self.build_placeholder_menu()
|
placeholder = self.build_placeholder_menu()
|
||||||
self._prompt_session = PromptSession(
|
self._prompt_session = PromptSession(
|
||||||
message=self.prompt,
|
message=self.prompt,
|
||||||
|
history=self.history,
|
||||||
multiline=False,
|
multiline=False,
|
||||||
completer=self._get_completer(),
|
completer=self._get_completer(),
|
||||||
validator=CommandValidator(self, self._get_validator_error_message()),
|
validator=CommandValidator(self, self._get_validator_error_message()),
|
||||||
|
@ -1 +1 @@
|
|||||||
__version__ = "0.1.75"
|
__version__ = "0.1.76"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "falyx"
|
name = "falyx"
|
||||||
version = "0.1.75"
|
version = "0.1.76"
|
||||||
description = "Reliable and introspectable async CLI action framework."
|
description = "Reliable and introspectable async CLI action framework."
|
||||||
authors = ["Roland Thomas Jr <roland@rtj.dev>"]
|
authors = ["Roland Thomas Jr <roland@rtj.dev>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
145
tests/test_actions/test_action_types.py
Normal file
145
tests/test_actions/test_action_types.py
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from falyx.action.action_types import ConfirmType, FileType, SelectionReturnType
|
||||||
|
|
||||||
|
|
||||||
|
def test_file_type_enum():
|
||||||
|
"""Test if the FileType enum has all expected members."""
|
||||||
|
assert FileType.TEXT.value == "text"
|
||||||
|
assert FileType.PATH.value == "path"
|
||||||
|
assert FileType.JSON.value == "json"
|
||||||
|
assert FileType.TOML.value == "toml"
|
||||||
|
assert FileType.YAML.value == "yaml"
|
||||||
|
assert FileType.CSV.value == "csv"
|
||||||
|
assert FileType.TSV.value == "tsv"
|
||||||
|
assert FileType.XML.value == "xml"
|
||||||
|
|
||||||
|
assert str(FileType.TEXT) == "text"
|
||||||
|
|
||||||
|
|
||||||
|
def test_file_type_choices():
|
||||||
|
"""Test if the FileType choices method returns all enum members."""
|
||||||
|
choices = FileType.choices()
|
||||||
|
assert len(choices) == 8
|
||||||
|
assert all(isinstance(choice, FileType) for choice in choices)
|
||||||
|
|
||||||
|
|
||||||
|
def test_file_type_missing():
|
||||||
|
"""Test if the _missing_ method raises ValueError for invalid values."""
|
||||||
|
with pytest.raises(ValueError, match="Invalid FileType: 'invalid'"):
|
||||||
|
FileType._missing_("invalid")
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="Invalid FileType: 123"):
|
||||||
|
FileType._missing_(123)
|
||||||
|
|
||||||
|
|
||||||
|
def test_file_type_aliases():
|
||||||
|
"""Test if the _get_alias method returns correct aliases."""
|
||||||
|
assert FileType._get_alias("file") == "path"
|
||||||
|
assert FileType._get_alias("filepath") == "path"
|
||||||
|
assert FileType._get_alias("unknown") == "unknown"
|
||||||
|
|
||||||
|
|
||||||
|
def test_file_type_missing_aliases():
|
||||||
|
"""Test if the _missing_ method handles aliases correctly."""
|
||||||
|
assert FileType._missing_("file") == FileType.PATH
|
||||||
|
assert FileType._missing_("filepath") == FileType.PATH
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="Invalid FileType: 'unknown'"):
|
||||||
|
FileType._missing_("unknown")
|
||||||
|
|
||||||
|
|
||||||
|
def test_confirm_type_enum():
|
||||||
|
"""Test if the ConfirmType enum has all expected members."""
|
||||||
|
assert ConfirmType.YES_NO.value == "yes_no"
|
||||||
|
assert ConfirmType.YES_CANCEL.value == "yes_cancel"
|
||||||
|
assert ConfirmType.YES_NO_CANCEL.value == "yes_no_cancel"
|
||||||
|
assert ConfirmType.TYPE_WORD.value == "type_word"
|
||||||
|
assert ConfirmType.TYPE_WORD_CANCEL.value == "type_word_cancel"
|
||||||
|
assert ConfirmType.OK_CANCEL.value == "ok_cancel"
|
||||||
|
assert ConfirmType.ACKNOWLEDGE.value == "acknowledge"
|
||||||
|
|
||||||
|
assert str(ConfirmType.YES_NO) == "yes_no"
|
||||||
|
|
||||||
|
|
||||||
|
def test_confirm_type_choices():
|
||||||
|
"""Test if the ConfirmType choices method returns all enum members."""
|
||||||
|
choices = ConfirmType.choices()
|
||||||
|
assert len(choices) == 7
|
||||||
|
assert all(isinstance(choice, ConfirmType) for choice in choices)
|
||||||
|
|
||||||
|
|
||||||
|
def test_confirm_type_missing():
|
||||||
|
"""Test if the _missing_ method raises ValueError for invalid values."""
|
||||||
|
with pytest.raises(ValueError, match="Invalid ConfirmType: 'invalid'"):
|
||||||
|
ConfirmType._missing_("invalid")
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="Invalid ConfirmType: 123"):
|
||||||
|
ConfirmType._missing_(123)
|
||||||
|
|
||||||
|
|
||||||
|
def test_confirm_type_aliases():
|
||||||
|
"""Test if the _get_alias method returns correct aliases."""
|
||||||
|
assert ConfirmType._get_alias("yes") == "yes_no"
|
||||||
|
assert ConfirmType._get_alias("ok") == "ok_cancel"
|
||||||
|
assert ConfirmType._get_alias("type") == "type_word"
|
||||||
|
assert ConfirmType._get_alias("word") == "type_word"
|
||||||
|
assert ConfirmType._get_alias("word_cancel") == "type_word_cancel"
|
||||||
|
assert ConfirmType._get_alias("ack") == "acknowledge"
|
||||||
|
|
||||||
|
|
||||||
|
def test_confirm_type_missing_aliases():
|
||||||
|
"""Test if the _missing_ method handles aliases correctly."""
|
||||||
|
assert ConfirmType("yes") == ConfirmType.YES_NO
|
||||||
|
assert ConfirmType("ok") == ConfirmType.OK_CANCEL
|
||||||
|
assert ConfirmType("word") == ConfirmType.TYPE_WORD
|
||||||
|
assert ConfirmType("ack") == ConfirmType.ACKNOWLEDGE
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="Invalid ConfirmType: 'unknown'"):
|
||||||
|
ConfirmType._missing_("unknown")
|
||||||
|
|
||||||
|
|
||||||
|
def test_selection_return_type_enum():
|
||||||
|
"""Test if the SelectionReturnType enum has all expected members."""
|
||||||
|
assert SelectionReturnType.KEY.value == "key"
|
||||||
|
assert SelectionReturnType.VALUE.value == "value"
|
||||||
|
assert SelectionReturnType.DESCRIPTION.value == "description"
|
||||||
|
assert SelectionReturnType.DESCRIPTION_VALUE.value == "description_value"
|
||||||
|
assert SelectionReturnType.ITEMS.value == "items"
|
||||||
|
|
||||||
|
assert str(SelectionReturnType.KEY) == "key"
|
||||||
|
|
||||||
|
|
||||||
|
def test_selection_return_type_choices():
|
||||||
|
"""Test if the SelectionReturnType choices method returns all enum members."""
|
||||||
|
choices = SelectionReturnType.choices()
|
||||||
|
assert len(choices) == 5
|
||||||
|
assert all(isinstance(choice, SelectionReturnType) for choice in choices)
|
||||||
|
|
||||||
|
|
||||||
|
def test_selection_return_type_missing():
|
||||||
|
"""Test if the _missing_ method raises ValueError for invalid values."""
|
||||||
|
with pytest.raises(ValueError, match="Invalid SelectionReturnType: 'invalid'"):
|
||||||
|
SelectionReturnType._missing_("invalid")
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="Invalid SelectionReturnType: 123"):
|
||||||
|
SelectionReturnType._missing_(123)
|
||||||
|
|
||||||
|
|
||||||
|
def test_selection_return_type_aliases():
|
||||||
|
"""Test if the _get_alias method returns correct aliases."""
|
||||||
|
assert SelectionReturnType._get_alias("desc") == "description"
|
||||||
|
assert SelectionReturnType._get_alias("desc_value") == "description_value"
|
||||||
|
assert SelectionReturnType._get_alias("unknown") == "unknown"
|
||||||
|
|
||||||
|
|
||||||
|
def test_selection_return_type_missing_aliases():
|
||||||
|
"""Test if the _missing_ method handles aliases correctly."""
|
||||||
|
assert SelectionReturnType._missing_("desc") == SelectionReturnType.DESCRIPTION
|
||||||
|
assert (
|
||||||
|
SelectionReturnType._missing_("desc_value")
|
||||||
|
== SelectionReturnType.DESCRIPTION_VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="Invalid SelectionReturnType: 'unknown'"):
|
||||||
|
SelectionReturnType._missing_("unknown")
|
@ -1,11 +1,40 @@
|
|||||||
import os
|
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
|
from argparse import ArgumentParser, Namespace, _SubParsersAction
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from falyx.__main__ import bootstrap, find_falyx_config, main
|
from falyx.__main__ import (
|
||||||
|
bootstrap,
|
||||||
|
find_falyx_config,
|
||||||
|
get_parsers,
|
||||||
|
init_callback,
|
||||||
|
init_config,
|
||||||
|
main,
|
||||||
|
)
|
||||||
|
from falyx.parser import CommandArgumentParser
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def fake_home(monkeypatch):
|
||||||
|
"""Redirect Path.home() to a temporary directory for all tests."""
|
||||||
|
temp_home = Path(tempfile.mkdtemp())
|
||||||
|
monkeypatch.setattr(Path, "home", lambda: temp_home)
|
||||||
|
yield temp_home
|
||||||
|
shutil.rmtree(temp_home, ignore_errors=True)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def setup_teardown():
|
||||||
|
"""Fixture to set up and tear down the environment for each test."""
|
||||||
|
cwd = Path.cwd()
|
||||||
|
yield
|
||||||
|
for file in cwd.glob("falyx.yaml"):
|
||||||
|
file.unlink(missing_ok=True)
|
||||||
|
for file in cwd.glob("falyx.toml"):
|
||||||
|
file.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
|
||||||
def test_find_falyx_config():
|
def test_find_falyx_config():
|
||||||
@ -50,3 +79,54 @@ def test_bootstrap_with_global_config():
|
|||||||
assert str(config_file.parent) in sys.path
|
assert str(config_file.parent) in sys.path
|
||||||
config_file.unlink()
|
config_file.unlink()
|
||||||
sys.path = sys_path_before
|
sys.path = sys_path_before
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_init_config():
|
||||||
|
"""Test if the init_config function adds the correct argument."""
|
||||||
|
parser = CommandArgumentParser()
|
||||||
|
init_config(parser)
|
||||||
|
args = await parser.parse_args(["test_project"])
|
||||||
|
assert args["name"] == "test_project"
|
||||||
|
|
||||||
|
# Test with default value
|
||||||
|
args = await parser.parse_args([])
|
||||||
|
assert args["name"] == "."
|
||||||
|
|
||||||
|
|
||||||
|
def test_init_callback(tmp_path):
|
||||||
|
"""Test if the init_callback function works correctly."""
|
||||||
|
# Test project initialization
|
||||||
|
args = Namespace(command="init", name=str(tmp_path))
|
||||||
|
init_callback(args)
|
||||||
|
assert (tmp_path / "falyx.yaml").exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_init_global_callback():
|
||||||
|
# Test global initialization
|
||||||
|
args = Namespace(command="init_global")
|
||||||
|
init_callback(args)
|
||||||
|
assert (Path.home() / ".config" / "falyx" / "tasks.py").exists()
|
||||||
|
assert (Path.home() / ".config" / "falyx" / "falyx.yaml").exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_parsers():
|
||||||
|
"""Test if the get_parsers function returns the correct parsers."""
|
||||||
|
root_parser, subparsers = get_parsers()
|
||||||
|
assert isinstance(root_parser, ArgumentParser)
|
||||||
|
assert isinstance(subparsers, _SubParsersAction)
|
||||||
|
|
||||||
|
# Check if the 'init' command is available
|
||||||
|
init_parser = subparsers.choices.get("init")
|
||||||
|
assert init_parser is not None
|
||||||
|
assert "name" == init_parser._get_positional_actions()[0].dest
|
||||||
|
|
||||||
|
|
||||||
|
def test_main():
|
||||||
|
"""Test if the main function runs with the correct arguments."""
|
||||||
|
|
||||||
|
sys.argv = ["falyx", "run", "?"]
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit) as exc_info:
|
||||||
|
main()
|
||||||
|
assert exc_info.value.code == 0
|
||||||
|
Reference in New Issue
Block a user