Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e5d930b
Consolidate align and margin tests into layout
InesaFitsner Feb 24, 2026
269e45f
Add layout control tests and clarify aspect_ratio
InesaFitsner Feb 25, 2026
72a73b1
Update layout_control macOS goldens, add tests
InesaFitsner Feb 25, 2026
b8bcdf9
Update scale value in layout test and golden image
InesaFitsner Feb 26, 2026
67d217f
Add animate_position test and update goldens
InesaFitsner Feb 26, 2026
6fdd40f
Add control visibility tests and golden images
InesaFitsner Feb 26, 2026
1cdda1f
Add expanding-controls images, docs and tests
InesaFitsner Feb 26, 2026
6b3caa6
Add test for expand_loose behavior in Row
InesaFitsner Feb 26, 2026
d9a3c02
Add expand examples and reference them in docs
InesaFitsner Feb 27, 2026
669e9ee
Use ft.Border and ft.BorderRadius in example
InesaFitsner Feb 27, 2026
72529e5
Wrap expand examples in styled containers
InesaFitsner Mar 3, 2026
d03cbeb
Add expand examples, docs images and tests
InesaFitsner Mar 3, 2026
a2d4304
Add expand_loose example, test, and image
InesaFitsner Mar 3, 2026
ebf5402
Add test for disabled propagation to children
InesaFitsner Mar 3, 2026
67be638
Add tooltip tests and golden screenshot
InesaFitsner Mar 3, 2026
6d79352
Add tooltip custom properties screenshot test
InesaFitsner Mar 3, 2026
898bb36
Add badge integration test and golden image
InesaFitsner Mar 4, 2026
b790c9d
Add opacity control test and golden image
InesaFitsner Mar 4, 2026
b324026
Add RTL layout screenshot test and golden
InesaFitsner Mar 4, 2026
60dfecf
Add ResponsiveRow col integration test
InesaFitsner Mar 4, 2026
f88afa7
Rename expand examples and update tests/docs
InesaFitsner Mar 5, 2026
3aa7965
Merge branch 'main' into layout-control-tests
InesaFitsner Mar 5, 2026
a392e8f
Move tooltip tests to test_tooltip.py
InesaFitsner Mar 5, 2026
640e9a7
Add create-flet-control-integration-tests skill
InesaFitsner Mar 9, 2026
6efeb92
Add scope-split rule and test file naming
InesaFitsner Mar 9, 2026
aab3b9c
Rename tooltip files to control_isolated
InesaFitsner Mar 9, 2026
c5eb74f
Move expand examples to controls/control
InesaFitsner Mar 9, 2026
8723991
Move expand golden images and test to core
InesaFitsner Mar 9, 2026
e814db3
Rename tooltip tests and golden images
InesaFitsner Mar 9, 2026
3f3d76c
Fix example_images path in expanding-controls doc
InesaFitsner Mar 9, 2026
effbb49
Merge branch 'main' into layout-control-tests
InesaFitsner Mar 9, 2026
0025ad5
Update SKILL.md examples paths & file list
InesaFitsner Mar 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions .codex/skills/create-flet-control-integration-tests/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
---
name: create-flet-control-integration-tests
description: Use when asked to create or update integration tests for any Flet control in sdk/python/packages/flet/integration_tests, including visual goldens and interactive behavior tests.
---

## When to use

Use this skill when adding, updating, or reviewing integration tests for a Flet control (core, material, cupertino, theme, services, types, or examples).

Use it for:

- visual rendering checks with golden screenshots
- interaction checks (hover, click, input, keyboard)
- control property behavior checks
- regression tests for control bugs

## Test placement

Pick the closest existing suite first:

- Core controls: `sdk/python/packages/flet/integration_tests/controls/core`
- Material controls: `sdk/python/packages/flet/integration_tests/controls/material`
- Cupertino controls: `sdk/python/packages/flet/integration_tests/controls/cupertino`
- Theme tests: `sdk/python/packages/flet/integration_tests/controls/theme`
- Type helpers: `sdk/python/packages/flet/integration_tests/controls/types`
- Services: `sdk/python/packages/flet/integration_tests/controls/services`
- Examples: `sdk/python/packages/flet/integration_tests/examples`

Prefer adding tests to an existing file for the same control. Create a new test file only when no suitable file exists.

### Scope split rule (required)

Do not mix `loop_scope="module"` and `loop_scope="function"` tests in the same file.

Naming convention:

- `test_<control>.py` for module-scoped visual/stable tests (uses `flet_app`)
- `test_<control>_isolated.py` for function-scoped tests that require a fresh app per test (uses `flet_app_function`)

## Fixture choice

Use fixtures from `integration_tests/conftest.py`:

- `flet_app` (`loop_scope="module"`): best for stable visual tests and bulk screenshot tests.
- `flet_app_function` (`loop_scope="function"`): best for tests that mutate state and must run in a separate app each.

## Authoring workflow

1. Identify behavior to verify.
2. Build deterministic layout/state:
- set `theme_mode` explicitly when visuals matter
- set fixed sizes/padding/spacing
- avoid random or time-varying content
3. Choose file by scope rule before writing tests.
4. Write test using async pytest.
5. Use screenshot assertion for UI behavior, functional assertion for non-visual behavior.
6. Name test so `request.node.name` can be used as screenshot key.
7. Run target test file.
8. If expected visuals changed, regenerate goldens with `FLET_TEST_GOLDEN=1` and re-run without golden mode.

## Assertion patterns

### Visual control assertion

```python
await flet_app.assert_control_screenshot(
request.node.name,
control,
)
```

### Visual full-page assertion

```python
flet_app_function.assert_screenshot(
request.node.name,
await flet_app_function.page.take_screenshot(
pixel_ratio=flet_app_function.screenshots_pixel_ratio
),
)
```

### Functional assertion

```python
finder = await flet_app_function.tester.find_by_tooltip("Info tooltip")
assert finder.count == 1
```

## Minimal templates

### Template: module-scope file (`test_<control>.py`)

```python
import pytest

import flet as ft
import flet.testing as ftt


@pytest.mark.asyncio(loop_scope="module")
async def test_<behavior>(flet_app: ftt.FletTestApp, request):
flet_app.page.theme_mode = ft.ThemeMode.LIGHT
await flet_app.assert_control_screenshot(
request.node.name,
ft.Container(width=240, height=80, alignment=ft.Alignment.CENTER),
)
```

### Template: function-scope file (`test_<control>_isolated.py`)

```python
import pytest

import flet as ft
import flet.testing as ftt


@pytest.mark.asyncio(loop_scope="function")
async def test_<behavior>(flet_app_function: ftt.FletTestApp):
flet_app_function.page.add(ft.IconButton(icon=ft.Icons.INFO, tooltip="Info"))
await flet_app_function.tester.pump_and_settle()

finder = await flet_app_function.tester.find_by_tooltip("Info")
assert finder.count == 1
```

## Run commands

Run one test file:

```bash
uv run pytest -s -o log_cli=true -o log_cli_level=INFO packages/flet/integration_tests/controls/core/test_control.py
```

Run a subset:

```bash
uv run pytest -s -o log_cli=true -o log_cli_level=INFO packages/flet/integration_tests/controls/core/test_control.py -k test_visible
```

Generate or update goldens:

```bash
FLET_TEST_GOLDEN=1 uv run pytest -s -o log_cli=true -o log_cli_level=INFO packages/flet/integration_tests/controls/core/test_control.py
```

## Quality checklist

- Test file location matches control area.
- `module` and `function` scope tests are in separate files.
- Function-scoped files use the `test_<control>_isolated.py` naming convention.
- Fixture scope matches test intent.
- Screenshot keys are stable (`request.node.name` preferred).
- Visual tests are deterministic (theme/size/content).
- Goldens are updated only for intentional visual changes.
- No unrelated formatting or refactors in the same change.

## References

- `sdk/python/packages/flet/integration_tests/README.md`
- `sdk/python/packages/flet/integration_tests/conftest.py`
- `sdk/python/packages/flet/integration_tests/controls/core/test_control.py`
- `sdk/python/packages/flet/integration_tests/controls/core/test_control_isolated.py`
- `sdk/python/packages/flet/integration_tests/controls/core/test_layout_control.py`
- `sdk/python/packages/flet/integration_tests/examples/core/test_control.py`
- `sdk/python/examples/controls/control`
8 changes: 4 additions & 4 deletions client/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -911,10 +911,10 @@ packages:
dependency: transitive
description:
name: matcher
sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6"
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
url: "https://pub.dev"
source: hosted
version: "0.12.18"
version: "0.12.19"
material_color_utilities:
dependency: transitive
description:
Expand Down Expand Up @@ -1628,10 +1628,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636"
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
url: "https://pub.dev"
source: hosted
version: "0.7.9"
version: "0.7.10"
torch_light:
dependency: transitive
description:
Expand Down
Empty file.
88 changes: 88 additions & 0 deletions sdk/python/examples/controls/control/expand_loose_chat_messages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
from dataclasses import field

import flet as ft


@ft.control
class Message(ft.Container):
author: str = ""
body: str = ""
border: ft.Border = field(default_factory=lambda: ft.Border.all(1, ft.Colors.BLACK))
border_radius: ft.BorderRadius = field(
default_factory=lambda: ft.BorderRadius.all(10)
)
bgcolor: ft.Colors = ft.Colors.GREEN_200
padding: ft.PaddingValue = 10
expand: bool = True
expand_loose: bool = True

def init(self):
self.content = ft.Column(
controls=[
ft.Text(self.author, weight=ft.FontWeight.BOLD),
ft.Text(self.body),
],
)


def main(page: ft.Page):
chat = ft.ListView(
padding=10,
spacing=10,
controls=[
ft.Row(
alignment=ft.MainAxisAlignment.START,
controls=[
Message(
author="John",
body="Hi, how are you?",
),
],
),
ft.Row(
alignment=ft.MainAxisAlignment.END,
controls=[
Message(
author="Jake",
body="Hi I am good thanks, how about you?",
),
],
),
ft.Row(
alignment=ft.MainAxisAlignment.START,
controls=[
Message(
author="John",
body=(
"Lorem Ipsum is simply dummy text of the printing and "
"typesetting industry. Lorem Ipsum has been the industry's "
"standard dummy text ever since the 1500s, when an unknown "
"printer took a galley of type and scrambled it to make a "
"type specimen book."
),
),
],
),
ft.Row(
alignment=ft.MainAxisAlignment.END,
controls=[
Message(
author="Jake",
body="Thank you!",
),
],
),
],
)

page.add(
ft.Container(
content=chat,
width=300,
height=500,
)
)


if __name__ == "__main__":
ft.run(main)
36 changes: 36 additions & 0 deletions sdk/python/examples/controls/control/expand_row_equal_split.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import flet as ft


def main(page: ft.Page):
page.add(
ft.Container(
width=500,
height=180,
padding=10,
border=ft.Border.all(2, ft.Colors.BLUE_GREY_200),
border_radius=10,
content=ft.Row(
spacing=8,
controls=[
ft.Container(
expand=True,
bgcolor=ft.Colors.ORANGE_300,
border_radius=8,
alignment=ft.Alignment.CENTER,
content=ft.Text("Card 1"),
),
ft.Container(
expand=True,
bgcolor=ft.Colors.GREEN_200,
border_radius=8,
alignment=ft.Alignment.CENTER,
content=ft.Text("Card 2"),
),
],
),
)
)


if __name__ == "__main__":
ft.run(main)
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import flet as ft


def main(page: ft.Page):
page.add(
ft.Container(
width=500,
padding=10,
border=ft.Border.all(2, ft.Colors.BLUE_GREY_200),
border_radius=10,
content=ft.Row(
spacing=8,
controls=[
ft.Container(
expand=1,
height=60,
bgcolor=ft.Colors.CYAN_300,
alignment=ft.Alignment.CENTER,
border_radius=8,
content=ft.Text("1"),
),
ft.Container(
expand=3,
height=60,
bgcolor=ft.Colors.AMBER_300,
alignment=ft.Alignment.CENTER,
border_radius=8,
content=ft.Text("3"),
),
ft.Container(
expand=1,
height=60,
bgcolor=ft.Colors.PINK_200,
alignment=ft.Alignment.CENTER,
border_radius=8,
content=ft.Text("1"),
),
],
),
)
)


if __name__ == "__main__":
ft.run(main)
22 changes: 22 additions & 0 deletions sdk/python/examples/controls/control/expand_textfield_in_row.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import flet as ft


def main(page: ft.Page):
page.add(
ft.Container(
width=480,
padding=10,
border=ft.Border.all(2, ft.Colors.BLUE_GREY_200),
border_radius=10,
content=ft.Row(
controls=[
ft.TextField(hint_text="Enter your name", expand=True),
ft.Button("Join chat"),
]
),
)
)


if __name__ == "__main__":
ft.run(main)
Loading
Loading