Updated skill and documentation

This commit is contained in:
2026-02-27 17:31:19 +01:00
parent c07b75ee72
commit efbc5a59ff
2 changed files with 597 additions and 312 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -522,7 +522,254 @@ expected = Input(
matches(actual, expected)
```
### 5. Combining matches() and find()
### 5. Testing MyFastHtml Components with Test Helpers
**Goal**: Understand why test helpers exist and how they simplify testing MyFastHtml controls.
When testing components built with `mk` helpers (buttons, labels, icons), writing the expected pattern manually quickly becomes verbose. Consider testing a label with an icon:
```python
# Without test helpers - verbose and fragile
from fastcore.basics import NotStr
from fasthtml.common import Span
from myfasthtml.test.matcher import matches, Regex
actual = label_component.render()
expected = Span(
Span(NotStr('<svg name="fluent-Info"')),
Span("My Label")
)
matches(actual, expected)
```
MyFastHtml provides **test helpers** — specialized `TestObject` subclasses that encapsulate these patterns. They know how `mk` renders components and abstract away the implementation details:
```python
# With test helpers - concise and readable
from myfasthtml.test.matcher import matches, TestLabel
actual = label_component.render()
matches(actual, TestLabel("My Label", icon="info"))
```
`TestObject` is the base class for all these helpers. You can also create your own helpers for custom components by subclassing it.
**TestObject constructor:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `cls` | type or str | The element type to match (e.g., `"div"`, `"span"`, `NotStr`) |
| `**kwargs` | any | Attributes to match on the element |
**Creating a custom helper:**
```python
from myfasthtml.test.matcher import TestObject, Contains
from fasthtml.common import Div, H2
class TestCard(TestObject):
def __init__(self, title: str):
super().__init__("div")
self.attrs["cls"] = Contains("card")
self.children = [
Div(H2(title), cls="card-header")
]
```
---
### 6. Testing Labels with TestLabel
**Goal**: Verify elements produced by `mk.label()` — text with an optional icon and optional command.
```python
from myfasthtml.test.matcher import TestLabel
```
| Parameter | Type | Description | Default |
|-----------|------|-------------|---------|
| `label` | str | The text content to match | - |
| `icon` | str | Icon name (`snake_case` or `PascalCase`) | `None` |
| `command` | Command | Command whose HTMX params to verify | `None` |
**Example 1: Label with text only**
```python
from myfasthtml.controls.helpers import mk
from myfasthtml.test.matcher import matches, TestLabel
actual = mk.label("Settings")
matches(actual, TestLabel("Settings"))
```
**Example 2: Label with icon**
```python
from myfasthtml.controls.helpers import mk
from myfasthtml.test.matcher import matches, TestLabel
actual = mk.label("Settings", icon="settings")
matches(actual, TestLabel("Settings", icon="settings"))
```
**Note:** Icon names can be passed in `snake_case` or `PascalCase``TestLabel` handles the conversion automatically.
**Example 3: Label with command**
```python
from myfasthtml.controls.helpers import mk
from myfasthtml.core.commands import Command
from myfasthtml.test.matcher import matches, TestLabel
def save():
return "Saved"
save_cmd = Command("save", "Save document", save)
actual = mk.label("Save", command=save_cmd)
matches(actual, TestLabel("Save", command=save_cmd))
```
---
### 7. Testing Icons with TestIcon and TestIconNotStr
**Goal**: Verify icon elements produced by `mk.icon()`.
MyFastHtml renders icons in two ways depending on context:
- **With a wrapper** (`div` or `span`): use `TestIcon`
- **As a raw SVG `NotStr`** without wrapper: use `TestIconNotStr`
```python
from myfasthtml.test.matcher import TestIcon, TestIconNotStr
```
#### TestIcon — Icon with wrapper
| Parameter | Type | Description | Default |
|-----------|------|-------------|---------|
| `name` | str | Icon name (`snake_case` or `PascalCase`) | `''` |
| `wrapper` | str | Wrapper element: `"div"` or `"span"` | `"div"` |
| `command` | Command | Command whose HTMX params to verify | `None` |
**Example 1: Simple icon in a div**
```python
from myfasthtml.controls.helpers import mk
from myfasthtml.test.matcher import matches, TestIcon
actual = mk.icon(info_svg)
matches(actual, TestIcon("info"))
```
**Example 2: Icon in a span with command**
```python
from myfasthtml.controls.helpers import mk
from myfasthtml.core.commands import Command
from myfasthtml.test.matcher import matches, TestIcon
delete_cmd = Command("delete", "Delete item", lambda: None)
actual = mk.icon(trash_svg, command=delete_cmd)
matches(actual, TestIcon("trash", wrapper="span", command=delete_cmd))
```
#### TestIconNotStr — Raw SVG without wrapper
Use `TestIconNotStr` when the icon appears directly as a `NotStr` in the element tree (e.g., embedded inside another element without its own wrapper).
| Parameter | Type | Description | Default |
|-----------|------|-------------|---------|
| `name` | str | Icon name (`snake_case` or `PascalCase`) | `''` |
**Example: Raw SVG icon embedded in a button**
```python
from myfasthtml.test.matcher import find, TestIconNotStr
actual = button_component.render()
icons = find(actual, TestIconNotStr("info"))
assert len(icons) == 1
```
---
### 8. Testing Commands with TestCommand
**Goal**: Verify that an element is linked to a specific command.
```python
from myfasthtml.test.matcher import TestCommand
```
| Parameter | Type | Description |
|-----------|------|-------------|
| `name` | str | The command name to match |
| `**kwargs` | any | Additional command attributes to verify |
**Example 1: Verify a button is bound to a command**
```python
from myfasthtml.controls.helpers import mk
from myfasthtml.core.commands import Command
from myfasthtml.test.matcher import find, TestCommand
delete_cmd = Command("delete_row", "Delete row", lambda: None)
button = mk.button("Delete", command=delete_cmd)
commands = find(button, TestCommand("delete_row"))
assert len(commands) == 1
```
**Example 2: Verify command with additional attributes**
```python
from myfasthtml.test.matcher import find, TestCommand
commands = find(component.render(), TestCommand("save", target="#result"))
assert len(commands) == 1
```
---
### 9. Testing Scripts with TestScript
**Goal**: Verify the content of `<script>` elements injected by a component.
```python
from myfasthtml.test.matcher import TestScript
```
| Parameter | Type | Description |
|-----------|------|-------------|
| `script` | str | The expected script content (checked with `startswith`) |
**Example 1: Verify a script is present**
```python
from myfasthtml.test.matcher import find, TestScript
actual = widget.render()
scripts = find(actual, TestScript("initWidget("))
assert len(scripts) == 1
```
**Example 2: Verify a specific script content**
```python
from myfasthtml.test.matcher import find, TestScript
scripts = find(actual, TestScript("document.getElementById('my-component')"))
assert len(scripts) == 1
```
---
### 10. Combining matches() and find()
**Goal**: First find elements, then validate them in detail.
@@ -574,7 +821,7 @@ for card in cards:
matches(card, expected_card)
```
### 6. Testing Edge Cases
### 11. Testing Edge Cases
**Testing empty elements:**