Updated skill and documentation
This commit is contained in:
@@ -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:**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user