# TUI Testing Quick Reference

## Common Commands

```bash
# Run all tests
pytest tests/ -v

# Run specific test file
pytest tests/test_screens.py -v

# Run tests matching pattern
pytest tests/ -k wizard -v

# Run with coverage
pytest tests/ --cov=src/styrene --cov-report=html

# Run failed tests only
pytest --lf -v

# Watch mode (rerun on file change)
pytest tests/ --watch

# Run interactive TUI
python -m styrene

# Run with hot reload
textual run --dev src/styrene/app.py
```

## tmux Visual Testing

```bash
# Create test session (80x24 terminal)
tmux new-session -d -s tui_test -x 80 -y 24

# Launch app in session
tmux send-keys -t tui_test 'python -m styrene' Enter

# Wait for render
sleep 1

# Capture with ANSI colors
tmux capture-pane -t tui_test -e -p > /tmp/capture.txt

# Capture plain text only
tmux capture-pane -t tui_test -p > /tmp/plain.txt

# Send keys
tmux send-keys -t tui_test 'p'           # Single key
tmux send-keys -t tui_test Escape        # Special key
tmux send-keys -t tui_test Enter
tmux send-keys -t tui_test C-c           # Ctrl+C

# Kill session
tmux kill-session -t tui_test

# Convert to PNG (requires: brew install termshot)
termshot --filename /tmp/tui.png -- cat /tmp/capture.txt
```

### Quick Visual Test Script

```bash
# One-liner: capture current state
tmux new-session -d -s t -x 80 -y 24 && \
tmux send-keys -t t 'python -m styrene' Enter && \
sleep 2 && \
tmux capture-pane -t t -e -p && \
tmux kill-session -t t
```

## Common Test Patterns

### Test Screen Mounts

```python
@pytest.mark.asyncio
async def test_screen_mounts():
    app = StyreneApp()
    async with app.run_test() as pilot:
        await pilot.app.push_screen(MyScreen())
        assert isinstance(pilot.app.screen, MyScreen)
```

### Test Widget Exists

```python
@pytest.mark.asyncio
async def test_widget_exists():
    app = StyreneApp()
    async with app.run_test() as pilot:
        widget = pilot.app.query_one("#widget-id")
        assert widget is not None
```

### Test Button Click

```python
@pytest.mark.asyncio
async def test_button_click():
    app = StyreneApp()
    async with app.run_test() as pilot:
        await pilot.click("#button-id")
        # Verify action occurred
```

### Test Keyboard Input

```python
@pytest.mark.asyncio
async def test_keyboard():
    app = StyreneApp()
    async with app.run_test() as pilot:
        await pilot.press("p")  # Press 'p' key
        await pilot.press("escape")  # Press escape
```

### Test Input Field

```python
@pytest.mark.asyncio
async def test_input():
    app = StyreneApp()
    async with app.run_test() as pilot:
        input_widget = pilot.app.query_one("#input-id", Input)
        input_widget.value = "test"
        await pilot.pause()
        assert input_widget.value == "test"
```

### Mock External Service

```python
from unittest.mock import patch

@pytest.mark.asyncio
async def test_with_mock():
    with patch('module.function') as mock_fn:
        mock_fn.return_value = "mocked"
        app = StyreneApp()
        async with app.run_test() as pilot:
            # Test with mock
            pass
```

### Test with Temporary Files

```python
@pytest.mark.asyncio
async def test_with_files(tmp_path):
    test_file = tmp_path / "test.txt"
    test_file.write_text("content")

    with patch('module.FILE_PATH', test_file):
        app = StyreneApp()
        async with app.run_test() as pilot:
            # Test with temp file
            pass
```

## Pilot Methods

```python
# Navigation
await pilot.press("key")          # Press key
await pilot.click("#selector")    # Click element
await pilot.hover("#selector")    # Hover over element

# Waiting
await pilot.pause()               # Pause for app to update
await pilot.pause(delay=1.0)      # Pause for specific time
await pilot.wait_for_scheduled_animations()

# Inspection
pilot.app                         # Access app instance
pilot.app.screen                  # Current screen
pilot.app.query_one("#id")        # Query widget
pilot.app.query("*").results()    # All widgets
```

## Fixtures (in conftest.py)

```python
@pytest.fixture
def app():
    return StyreneApp()

@pytest.fixture
async def pilot(app):
    async with app.run_test() as pilot:
        yield pilot

@pytest.fixture
def mock_config(tmp_path):
    config_dir = tmp_path / ".styrene"
    config_dir.mkdir()
    return config_dir
```

## Debugging

```python
# Print widget tree
print(pilot.app.query("*").results())

# Print specific widget
widget = pilot.app.query_one("#widget")
print(f"Widget: {widget}")
print(f"Classes: {widget.classes}")
print(f"Styles: {widget.styles}")

# Take screenshot (SVG)
screenshot = pilot.app.export_screenshot()
Path("debug.svg").write_text(screenshot)
```

## CSS Testing

```python
def test_css_exists():
    from pathlib import Path
    css = Path("src/styrene/styles/imperial_crt.tcss").read_text()
    assert ".my-class" in css
    assert "#my-id" in css
```

## Common Assertions

```python
# Screen type
assert isinstance(pilot.app.screen, MyScreen)

# Widget exists
assert pilot.app.query_one("#widget-id") is not None

# Widget has class
assert widget.has_class("active")

# Widget property
assert widget.value == "expected"

# Widget visibility
assert widget.visible
assert not widget.disabled

# Screen stack
assert len(pilot.app.screen_stack) == 2
```

## Test Organization

```
tests/
├── conftest.py                 # Shared fixtures
├── test_app.py                 # App-level tests
├── test_screens.py             # Screen tests
├── test_interactions.py        # Interaction tests
├── test_integration.py         # End-to-end tests
├── test_layouts.py             # Layout/CSS tests
└── widgets/
    └── test_widget_name.py     # Widget-specific tests
```

## Manual Testing Flow

1. **Write automated tests** - Define expected behavior
2. **Implement feature** - Make tests pass
3. **Run test suite** - `pytest tests/ -v`
4. **Run validation** - `ruff check`, `mypy src/`
5. **Interactive test** - `python -m styrene`
6. **Visual check** - Verify layout, colors, spacing
7. **Edge cases** - Test with empty data, max data, errors
8. **Commit** - Only after all tests pass

## Troubleshooting

| Problem | Solution |
|---------|----------|
| Test hangs | Check for missing `await` |
| Test flakes | Add `await pilot.pause()` after state changes |
| Import errors | Verify `sys.path` includes src/ |
| Widget not found | Use `pilot.app.query("*").results()` to debug |
| CSS not applied | Check class names match TCSS file |
| Async errors | Ensure `@pytest.mark.asyncio` decorator |

## Performance

```python
import time

@pytest.mark.asyncio
async def test_performance():
    app = StyreneApp()
    async with app.run_test() as pilot:
        start = time.perf_counter()
        # Action to measure
        elapsed = time.perf_counter() - start
        assert elapsed < 0.1  # Should be under 100ms
```

## CI/CD Integration

```yaml
- name: Run Tests
  run: pytest tests/ -v --cov=src/styrene

- name: Lint
  run: ruff check src/

- name: Type Check
  run: mypy src/
```
