feat: redesign help
command, improve completer UX, and document Falyx CLI
- Renamed CLI subcommand from `list` to `help` for clarity and discoverability. - Added `--key` and `--tldr` support to the `help` command for detailed and example-based output. - Introduced `FalyxMode.HELP` to clearly delineate help-related behavior. - Enhanced `_render_help()` to support: - Tag filtering (`--tag`) - Per-command help (`--key`) - TLDR example rendering (`--tldr`) - Updated built-in Help command to: - Use `FalyxMode.HELP` internally - Provide fallback messages for missing help or TLDR data - Remove `LIST` alias (replaced by `help`) - Documented `FalyxCompleter`: - Improved docstrings for public methods and completions - Updated internal documentation to reflect all supported completion cases - Updated `CommandArgumentParser.render_tldr()` with fallback message for missing TLDR entries. - Updated all parser docstrings and variable names to reference `help` (not `list`) as the proper CLI entrypoint. - Added test coverage: - `tests/test_falyx/test_help.py` for CLI `help` command with `tag`, `key`, `tldr`, and fallback scenarios - `tests/test_falyx/test_run.py` for basic CLI parser integration - Bumped version to 0.1.81
This commit is contained in:
@ -346,6 +346,7 @@ class Command(BaseModel):
|
|||||||
FalyxMode.RUN,
|
FalyxMode.RUN,
|
||||||
FalyxMode.PREVIEW,
|
FalyxMode.PREVIEW,
|
||||||
FalyxMode.RUN_ALL,
|
FalyxMode.RUN_ALL,
|
||||||
|
FalyxMode.HELP,
|
||||||
}
|
}
|
||||||
|
|
||||||
program = f"{self.program} run " if is_cli_mode else ""
|
program = f"{self.program} run " if is_cli_mode else ""
|
||||||
|
@ -8,9 +8,13 @@ This completer supports:
|
|||||||
- Argument flag completion for registered commands (e.g. `--tag`, `--name`)
|
- Argument flag completion for registered commands (e.g. `--tag`, `--name`)
|
||||||
- Context-aware suggestions based on cursor position and argument structure
|
- Context-aware suggestions based on cursor position and argument structure
|
||||||
- Interactive value completions (e.g. choices and suggestions defined per argument)
|
- Interactive value completions (e.g. choices and suggestions defined per argument)
|
||||||
|
- File/path-friendly behavior, quoting completions with spaces automatically
|
||||||
|
|
||||||
|
|
||||||
|
Completions are generated from:
|
||||||
|
- Registered commands in `Falyx`
|
||||||
|
- Argument metadata and `suggest_next()` from `CommandArgumentParser`
|
||||||
|
|
||||||
Completions are sourced from `CommandArgumentParser.suggest_next`, which analyzes
|
|
||||||
parsed tokens to determine appropriate next arguments, flags, or values.
|
|
||||||
|
|
||||||
Integrated with the `Falyx.prompt_session` to enhance the interactive experience.
|
Integrated with the `Falyx.prompt_session` to enhance the interactive experience.
|
||||||
"""
|
"""
|
||||||
@ -42,9 +46,12 @@ class FalyxCompleter(Completer):
|
|||||||
- Remaining required or optional flags
|
- Remaining required or optional flags
|
||||||
- Flag value suggestions (choices or custom completions)
|
- Flag value suggestions (choices or custom completions)
|
||||||
- Next positional argument hints
|
- Next positional argument hints
|
||||||
|
- Inserts longest common prefix (LCP) completions when applicable
|
||||||
|
- Handles special cases like quoted strings and spaces
|
||||||
|
- Supports dynamic argument suggestions (e.g. flags, file paths, etc.)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
falyx (Falyx): The Falyx menu instance containing all command mappings and parsers.
|
falyx (Falyx): The active Falyx instance providing command and parser context.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, falyx: "Falyx"):
|
def __init__(self, falyx: "Falyx"):
|
||||||
@ -52,14 +59,21 @@ class FalyxCompleter(Completer):
|
|||||||
|
|
||||||
def get_completions(self, document: Document, complete_event) -> Iterable[Completion]:
|
def get_completions(self, document: Document, complete_event) -> Iterable[Completion]:
|
||||||
"""
|
"""
|
||||||
Yield completions based on the current document input.
|
Compute completions for the current user input.
|
||||||
|
|
||||||
|
Analyzes the input buffer, determines whether the user is typing:
|
||||||
|
• A command key/alias
|
||||||
|
• A flag/option
|
||||||
|
• An argument value
|
||||||
|
|
||||||
|
and yields appropriate completions.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
document (Document): The prompt_toolkit document containing the input buffer.
|
document (Document): The current Prompt Toolkit document (input buffer & cursor).
|
||||||
complete_event: The completion trigger event (unused).
|
complete_event: The triggering event (TAB key, menu display, etc.) — not used here.
|
||||||
|
|
||||||
Yields:
|
Yields:
|
||||||
Completion objects matching command keys or argument suggestions.
|
Completion: One or more completions matching the current stub text.
|
||||||
"""
|
"""
|
||||||
text = document.text_before_cursor
|
text = document.text_before_cursor
|
||||||
try:
|
try:
|
||||||
@ -97,8 +111,11 @@ class FalyxCompleter(Completer):
|
|||||||
"""
|
"""
|
||||||
Suggest top-level command keys and aliases based on the given prefix.
|
Suggest top-level command keys and aliases based on the given prefix.
|
||||||
|
|
||||||
|
Filters all known commands (and `exit`, `help`, `history` built-ins)
|
||||||
|
to only those starting with the given prefix.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
prefix (str): The user input to match against available commands.
|
prefix (str): The current typed prefix.
|
||||||
|
|
||||||
Yields:
|
Yields:
|
||||||
Completion: Matching keys or aliases from all registered commands.
|
Completion: Matching keys or aliases from all registered commands.
|
||||||
@ -121,7 +138,10 @@ class FalyxCompleter(Completer):
|
|||||||
|
|
||||||
def _ensure_quote(self, text: str) -> str:
|
def _ensure_quote(self, text: str) -> str:
|
||||||
"""
|
"""
|
||||||
Ensure the text is properly quoted for shell commands.
|
Ensure that a suggestion is shell-safe by quoting if needed.
|
||||||
|
|
||||||
|
Adds quotes around completions containing whitespace so they can
|
||||||
|
be inserted into the CLI without breaking tokenization.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
text (str): The input text to quote.
|
text (str): The input text to quote.
|
||||||
@ -134,6 +154,22 @@ class FalyxCompleter(Completer):
|
|||||||
return text
|
return text
|
||||||
|
|
||||||
def _yield_lcp_completions(self, suggestions, stub):
|
def _yield_lcp_completions(self, suggestions, stub):
|
||||||
|
"""
|
||||||
|
Yield completions for the current stub using longest-common-prefix logic.
|
||||||
|
|
||||||
|
Behavior:
|
||||||
|
- If only one match → yield it fully.
|
||||||
|
- If multiple matches share a longer prefix → insert the prefix, but also
|
||||||
|
display all matches in the menu.
|
||||||
|
- If no shared prefix → list all matches individually.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
suggestions (list[str]): The raw suggestions to consider.
|
||||||
|
stub (str): The currently typed prefix (used to offset insertion).
|
||||||
|
|
||||||
|
Yields:
|
||||||
|
Completion: Completion objects for the Prompt Toolkit menu.
|
||||||
|
"""
|
||||||
matches = [s for s in suggestions if s.startswith(stub)]
|
matches = [s for s in suggestions if s.startswith(stub)]
|
||||||
if not matches:
|
if not matches:
|
||||||
return
|
return
|
||||||
|
@ -207,6 +207,7 @@ class Falyx:
|
|||||||
FalyxMode.RUN,
|
FalyxMode.RUN,
|
||||||
FalyxMode.PREVIEW,
|
FalyxMode.PREVIEW,
|
||||||
FalyxMode.RUN_ALL,
|
FalyxMode.RUN_ALL,
|
||||||
|
FalyxMode.HELP,
|
||||||
}
|
}
|
||||||
|
|
||||||
def validate_options(
|
def validate_options(
|
||||||
@ -359,11 +360,12 @@ class Falyx:
|
|||||||
f"Use '{self.program} --verbose' to enable debug logging for a menu session.",
|
f"Use '{self.program} --verbose' to enable debug logging for a menu session.",
|
||||||
f"'{self.program} --debug-hooks' will trace every before/after hook in action.",
|
f"'{self.program} --debug-hooks' will trace every before/after hook in action.",
|
||||||
f"Run commands directly from the CLI: '{self.program} run [COMMAND] [OPTIONS]'.",
|
f"Run commands directly from the CLI: '{self.program} run [COMMAND] [OPTIONS]'.",
|
||||||
|
"All [COMMAND] keys and aliases are case-insensitive.",
|
||||||
]
|
]
|
||||||
if self.is_cli_mode:
|
if self.is_cli_mode:
|
||||||
tips.extend(
|
tips.extend(
|
||||||
[
|
[
|
||||||
f"Use '{self.program} run ?' to list all commands at any time.",
|
f"Use '{self.program} help' to list all commands at any time.",
|
||||||
f"Use '{self.program} --never-prompt run [COMMAND] [OPTIONS]' to disable all prompts for [bold italic]just this command[/].",
|
f"Use '{self.program} --never-prompt run [COMMAND] [OPTIONS]' to disable all prompts for [bold italic]just this command[/].",
|
||||||
f"Use '{self.program} run --skip-confirm [COMMAND] [OPTIONS]' to skip confirmations.",
|
f"Use '{self.program} run --skip-confirm [COMMAND] [OPTIONS]' to skip confirmations.",
|
||||||
f"Use '{self.program} run --summary [COMMAND] [OPTIONS]' to print a post-run summary.",
|
f"Use '{self.program} run --summary [COMMAND] [OPTIONS]' to print a post-run summary.",
|
||||||
@ -382,7 +384,27 @@ class Falyx:
|
|||||||
)
|
)
|
||||||
return choice(tips)
|
return choice(tips)
|
||||||
|
|
||||||
async def _render_help(self, tag: str = "") -> None:
|
async def _render_help(
|
||||||
|
self, tag: str = "", key: str | None = None, tldr: bool = False
|
||||||
|
) -> None:
|
||||||
|
if key:
|
||||||
|
_, command, args, kwargs = await self.get_command(key)
|
||||||
|
if command and tldr and command.arg_parser:
|
||||||
|
command.arg_parser.render_tldr()
|
||||||
|
return None
|
||||||
|
elif command and tldr and not command.arg_parser:
|
||||||
|
self.console.print(
|
||||||
|
f"[bold]No TLDR examples available for '{command.description}'.[/bold]"
|
||||||
|
)
|
||||||
|
elif command and command.arg_parser:
|
||||||
|
command.arg_parser.render_help()
|
||||||
|
return None
|
||||||
|
elif command and not command.arg_parser:
|
||||||
|
self.console.print(
|
||||||
|
f"[bold]No detailed help available for '{command.description}'.[/bold]"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.console.print(f"[bold]No command found for '{key}'.[/bold]")
|
||||||
if tag:
|
if tag:
|
||||||
tag_lower = tag.lower()
|
tag_lower = tag.lower()
|
||||||
self.console.print(f"[bold]{tag_lower}:[/bold]")
|
self.console.print(f"[bold]{tag_lower}:[/bold]")
|
||||||
@ -451,7 +473,7 @@ class Falyx:
|
|||||||
command_key="H",
|
command_key="H",
|
||||||
command_description="Help",
|
command_description="Help",
|
||||||
command_style=OneColors.LIGHT_YELLOW,
|
command_style=OneColors.LIGHT_YELLOW,
|
||||||
aliases=["?", "HELP", "LIST"],
|
aliases=["?", "HELP"],
|
||||||
program=self.program,
|
program=self.program,
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@ -463,7 +485,7 @@ class Falyx:
|
|||||||
)
|
)
|
||||||
return Command(
|
return Command(
|
||||||
key="H",
|
key="H",
|
||||||
aliases=["?", "HELP", "LIST"],
|
aliases=["?", "HELP"],
|
||||||
description="Help",
|
description="Help",
|
||||||
help_text="Show this help menu",
|
help_text="Show this help menu",
|
||||||
action=Action("Help", self._render_help),
|
action=Action("Help", self._render_help),
|
||||||
@ -907,11 +929,7 @@ class Falyx:
|
|||||||
logger.info("Command '%s' selected.", run_command.key)
|
logger.info("Command '%s' selected.", run_command.key)
|
||||||
if is_preview:
|
if is_preview:
|
||||||
return True, run_command, args, kwargs
|
return True, run_command, args, kwargs
|
||||||
elif self.options.get("mode") in {
|
elif self.is_cli_mode:
|
||||||
FalyxMode.RUN,
|
|
||||||
FalyxMode.RUN_ALL,
|
|
||||||
FalyxMode.PREVIEW,
|
|
||||||
}:
|
|
||||||
return False, run_command, args, kwargs
|
return False, run_command, args, kwargs
|
||||||
try:
|
try:
|
||||||
args, kwargs = await run_command.parse_args(input_args, from_validate)
|
args, kwargs = await run_command.parse_args(input_args, from_validate)
|
||||||
@ -1166,7 +1184,7 @@ class Falyx:
|
|||||||
This method parses CLI arguments, configures the runtime environment, and dispatches
|
This method parses CLI arguments, configures the runtime environment, and dispatches
|
||||||
execution to the appropriate command mode:
|
execution to the appropriate command mode:
|
||||||
|
|
||||||
- list - Show help output, optionally filtered by tag.
|
- help - Show help output, optionally filtered by tag.
|
||||||
- version - Print the program version and exit.
|
- version - Print the program version and exit.
|
||||||
- preview - Display a preview of the specified command without executing it.
|
- preview - Display a preview of the specified command without executing it.
|
||||||
- run - Execute a single command with parsed arguments and lifecycle hooks.
|
- run - Execute a single command with parsed arguments and lifecycle hooks.
|
||||||
@ -1255,8 +1273,11 @@ class Falyx:
|
|||||||
logger.debug("Enabling global debug hooks for all commands")
|
logger.debug("Enabling global debug hooks for all commands")
|
||||||
self.register_all_with_debug_hooks()
|
self.register_all_with_debug_hooks()
|
||||||
|
|
||||||
if self.cli_args.command == "list":
|
if self.cli_args.command == "help":
|
||||||
await self._render_help(tag=self.cli_args.tag)
|
self.options.set("mode", FalyxMode.HELP)
|
||||||
|
await self._render_help(
|
||||||
|
tag=self.cli_args.tag, key=self.cli_args.key, tldr=self.cli_args.tldr
|
||||||
|
)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
if self.cli_args.command == "version" or self.cli_args.version:
|
if self.cli_args.command == "version" or self.cli_args.version:
|
||||||
|
@ -10,3 +10,4 @@ class FalyxMode(Enum):
|
|||||||
RUN = "run"
|
RUN = "run"
|
||||||
PREVIEW = "preview"
|
PREVIEW = "preview"
|
||||||
RUN_ALL = "run-all"
|
RUN_ALL = "run-all"
|
||||||
|
HELP = "help"
|
||||||
|
@ -1192,7 +1192,6 @@ class CommandArgumentParser:
|
|||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
last = args[-1]
|
|
||||||
next_to_last = args[-2] if len(args) > 1 else ""
|
next_to_last = args[-2] if len(args) > 1 else ""
|
||||||
suggestions: list[str] = []
|
suggestions: list[str] = []
|
||||||
|
|
||||||
@ -1399,12 +1398,15 @@ class CommandArgumentParser:
|
|||||||
Displays brief usage examples with descriptions.
|
Displays brief usage examples with descriptions.
|
||||||
"""
|
"""
|
||||||
if not self._tldr_examples:
|
if not self._tldr_examples:
|
||||||
self.console.print("[bold]No TLDR examples available.[/bold]")
|
self.console.print(
|
||||||
|
f"[bold]No TLDR examples available for {self.command_key}.[/bold]"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
is_cli_mode = self.options_manager.get("mode") in {
|
is_cli_mode = self.options_manager.get("mode") in {
|
||||||
FalyxMode.RUN,
|
FalyxMode.RUN,
|
||||||
FalyxMode.PREVIEW,
|
FalyxMode.PREVIEW,
|
||||||
FalyxMode.RUN_ALL,
|
FalyxMode.RUN_ALL,
|
||||||
|
FalyxMode.HELP,
|
||||||
}
|
}
|
||||||
|
|
||||||
program = self.program or "falyx"
|
program = self.program or "falyx"
|
||||||
|
@ -4,7 +4,7 @@ Provides the argument parser infrastructure for the Falyx CLI.
|
|||||||
|
|
||||||
This module defines the `FalyxParsers` dataclass and related utilities for building
|
This module defines the `FalyxParsers` dataclass and related utilities for building
|
||||||
structured CLI interfaces with argparse. It supports top-level CLI commands like
|
structured CLI interfaces with argparse. It supports top-level CLI commands like
|
||||||
`run`, `run-all`, `preview`, `list`, and `version`, and integrates seamlessly with
|
`run`, `run-all`, `preview`, `help`, and `version`, and integrates seamlessly with
|
||||||
registered `Command` objects for dynamic help, usage generation, and argument handling.
|
registered `Command` objects for dynamic help, usage generation, and argument handling.
|
||||||
|
|
||||||
Key Components:
|
Key Components:
|
||||||
@ -39,7 +39,7 @@ class FalyxParsers:
|
|||||||
run: ArgumentParser
|
run: ArgumentParser
|
||||||
run_all: ArgumentParser
|
run_all: ArgumentParser
|
||||||
preview: ArgumentParser
|
preview: ArgumentParser
|
||||||
list: ArgumentParser
|
help: ArgumentParser
|
||||||
version: ArgumentParser
|
version: ArgumentParser
|
||||||
|
|
||||||
def parse_args(self, args: Sequence[str] | None = None) -> Namespace:
|
def parse_args(self, args: Sequence[str] | None = None) -> Namespace:
|
||||||
@ -196,7 +196,7 @@ def get_arg_parsers(
|
|||||||
|
|
||||||
This function builds the root parser and all subcommand parsers used for structured
|
This function builds the root parser and all subcommand parsers used for structured
|
||||||
CLI workflows in Falyx. It supports standard subcommands including `run`, `run-all`,
|
CLI workflows in Falyx. It supports standard subcommands including `run`, `run-all`,
|
||||||
`preview`, `list`, and `version`, and integrates with registered `Command` objects
|
`preview`, `help`, and `version`, and integrates with registered `Command` objects
|
||||||
to populate dynamic help and usage documentation.
|
to populate dynamic help and usage documentation.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -219,7 +219,7 @@ def get_arg_parsers(
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
FalyxParsers: A structured container of all parsers, including `run`, `run-all`,
|
FalyxParsers: A structured container of all parsers, including `run`, `run-all`,
|
||||||
`preview`, `list`, `version`, and the root parser.
|
`preview`, `help`, `version`, and the root parser.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: If `root_parser` is not an instance of ArgumentParser or
|
TypeError: If `root_parser` is not an instance of ArgumentParser or
|
||||||
@ -375,12 +375,24 @@ def get_arg_parsers(
|
|||||||
)
|
)
|
||||||
preview_parser.add_argument("name", help="Key, alias, or description of the command")
|
preview_parser.add_argument("name", help="Key, alias, or description of the command")
|
||||||
|
|
||||||
list_parser = subparsers.add_parser(
|
help_parser = subparsers.add_parser("help", help="List all available commands")
|
||||||
"list", help="List all available commands with tags"
|
|
||||||
|
help_parser.add_argument(
|
||||||
|
"-t", "--tag", help="Filter commands by tag (case-insensitive)", default=None
|
||||||
)
|
)
|
||||||
|
|
||||||
list_parser.add_argument(
|
help_parser.add_argument(
|
||||||
"-t", "--tag", help="Filter commands by tag (case-insensitive)", default=None
|
"-k",
|
||||||
|
"--key",
|
||||||
|
help="Show help for a specific command by its key or alias",
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
help_parser.add_argument(
|
||||||
|
"-T",
|
||||||
|
"--tldr",
|
||||||
|
action="store_true",
|
||||||
|
help="Show a simplified TLDR examples of a command if available",
|
||||||
)
|
)
|
||||||
|
|
||||||
version_parser = subparsers.add_parser("version", help=f"Show {prog} version")
|
version_parser = subparsers.add_parser("version", help=f"Show {prog} version")
|
||||||
@ -391,6 +403,6 @@ def get_arg_parsers(
|
|||||||
run=run_parser,
|
run=run_parser,
|
||||||
run_all=run_all_parser,
|
run_all=run_all_parser,
|
||||||
preview=preview_parser,
|
preview=preview_parser,
|
||||||
list=list_parser,
|
help=help_parser,
|
||||||
version=version_parser,
|
version=version_parser,
|
||||||
)
|
)
|
||||||
|
@ -1 +1 @@
|
|||||||
__version__ = "0.1.80"
|
__version__ = "0.1.81"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "falyx"
|
name = "falyx"
|
||||||
version = "0.1.80"
|
version = "0.1.81"
|
||||||
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"
|
||||||
|
94
tests/test_falyx/test_help.py
Normal file
94
tests/test_falyx/test_help.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from falyx import Falyx
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_help_command(capsys):
|
||||||
|
flx = Falyx()
|
||||||
|
await flx.run_key("H")
|
||||||
|
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert "Show this help menu" in captured.out
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_help_command_with_new_command(capsys):
|
||||||
|
flx = Falyx()
|
||||||
|
|
||||||
|
async def new_command(falyx: Falyx):
|
||||||
|
pass
|
||||||
|
|
||||||
|
flx.add_command(
|
||||||
|
"N",
|
||||||
|
"New Command",
|
||||||
|
new_command,
|
||||||
|
aliases=["TEST"],
|
||||||
|
help_text="This is a new command.",
|
||||||
|
)
|
||||||
|
await flx.run_key("H")
|
||||||
|
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert "This is a new command." in captured.out
|
||||||
|
assert "TEST" in captured.out and "N" in captured.out
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_render_help(capsys):
|
||||||
|
flx = Falyx()
|
||||||
|
|
||||||
|
async def sample_command(falyx: Falyx):
|
||||||
|
pass
|
||||||
|
|
||||||
|
flx.add_command(
|
||||||
|
"S",
|
||||||
|
"Sample Command",
|
||||||
|
sample_command,
|
||||||
|
aliases=["SC"],
|
||||||
|
help_text="This is a sample command.",
|
||||||
|
)
|
||||||
|
await flx._render_help()
|
||||||
|
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert "This is a sample command." in captured.out
|
||||||
|
assert "SC" in captured.out and "S" in captured.out
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_help_command_by_tag(capsys):
|
||||||
|
flx = Falyx()
|
||||||
|
|
||||||
|
async def tagged_command(falyx: Falyx):
|
||||||
|
pass
|
||||||
|
|
||||||
|
flx.add_command(
|
||||||
|
"T",
|
||||||
|
"Tagged Command",
|
||||||
|
tagged_command,
|
||||||
|
tags=["tag1"],
|
||||||
|
help_text="This command is tagged.",
|
||||||
|
)
|
||||||
|
await flx.run_key("H", args=("tag1",))
|
||||||
|
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert "tag1" in captured.out
|
||||||
|
assert "This command is tagged." in captured.out
|
||||||
|
assert "HELP" not in captured.out
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_help_command_empty_tags(capsys):
|
||||||
|
flx = Falyx()
|
||||||
|
|
||||||
|
async def untagged_command(falyx: Falyx):
|
||||||
|
pass
|
||||||
|
|
||||||
|
flx.add_command(
|
||||||
|
"U", "Untagged Command", untagged_command, help_text="This command has no tags."
|
||||||
|
)
|
||||||
|
await flx.run_key("H", args=("nonexistent_tag",))
|
||||||
|
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
print(captured.out)
|
||||||
|
assert "nonexistent_tag" in captured.out
|
||||||
|
assert "Nothing to show here" in captured.out
|
19
tests/test_falyx/test_run.py
Normal file
19
tests/test_falyx/test_run.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from falyx import Falyx
|
||||||
|
from falyx.parser import get_arg_parsers
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_run_basic(capsys):
|
||||||
|
sys.argv = ["falyx", "run", "-h"]
|
||||||
|
falyx_parsers = get_arg_parsers()
|
||||||
|
assert falyx_parsers is not None, "Falyx parsers should be initialized"
|
||||||
|
flx = Falyx()
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
await flx.run(falyx_parsers)
|
||||||
|
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert "Run a command by its key or alias." in captured.out
|
Reference in New Issue
Block a user