diff --git a/.codex/skills/create-flet-control-integration-tests/SKILL.md b/.codex/skills/create-flet-control-integration-tests/SKILL.md new file mode 100644 index 0000000000..0591a2a05b --- /dev/null +++ b/.codex/skills/create-flet-control-integration-tests/SKILL.md @@ -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_.py` for module-scoped visual/stable tests (uses `flet_app`) +- `test__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_.py`) + +```python +import pytest + +import flet as ft +import flet.testing as ftt + + +@pytest.mark.asyncio(loop_scope="module") +async def test_(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__isolated.py`) + +```python +import pytest + +import flet as ft +import flet.testing as ftt + + +@pytest.mark.asyncio(loop_scope="function") +async def test_(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__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` diff --git a/client/pubspec.lock b/client/pubspec.lock index d7d7a5df8a..0e4b12b38c 100644 --- a/client/pubspec.lock +++ b/client/pubspec.lock @@ -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: @@ -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: diff --git a/sdk/python/examples/controls/control/__init__.py b/sdk/python/examples/controls/control/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sdk/python/examples/controls/control/expand_loose_chat_messages.py b/sdk/python/examples/controls/control/expand_loose_chat_messages.py new file mode 100644 index 0000000000..4b67623693 --- /dev/null +++ b/sdk/python/examples/controls/control/expand_loose_chat_messages.py @@ -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) diff --git a/sdk/python/examples/controls/control/expand_row_equal_split.py b/sdk/python/examples/controls/control/expand_row_equal_split.py new file mode 100644 index 0000000000..02ef2de503 --- /dev/null +++ b/sdk/python/examples/controls/control/expand_row_equal_split.py @@ -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) diff --git a/sdk/python/examples/controls/control/expand_row_proportional_1_3_1.py b/sdk/python/examples/controls/control/expand_row_proportional_1_3_1.py new file mode 100644 index 0000000000..c025b9347e --- /dev/null +++ b/sdk/python/examples/controls/control/expand_row_proportional_1_3_1.py @@ -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) diff --git a/sdk/python/examples/controls/control/expand_textfield_in_row.py b/sdk/python/examples/controls/control/expand_textfield_in_row.py new file mode 100644 index 0000000000..1da25f2cd9 --- /dev/null +++ b/sdk/python/examples/controls/control/expand_textfield_in_row.py @@ -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) diff --git a/sdk/python/packages/flet/docs/cookbook/expanding-controls.md b/sdk/python/packages/flet/docs/cookbook/expanding-controls.md index 875fce8d1c..b23868ad10 100644 --- a/sdk/python/packages/flet/docs/cookbook/expanding-controls.md +++ b/sdk/python/packages/flet/docs/cookbook/expanding-controls.md @@ -1,3 +1,9 @@ +--- +title: Expanding Controls +examples: ../../examples/controls/control +example_images: ../test-images/examples/core/golden/macos/control +--- + ## `expand` When a child control is placed into a [`Row`][flet.Row], [`Column`][flet.Column], [`View`][flet.View] @@ -21,78 +27,38 @@ In this example, a [`TextField`][flet.TextField] stretches to fill all remaining while the [`Button`][flet.Button] stays sized to its content: ```python -ft.Row( - controls=[ - ft.TextField(hint_text="Enter your name", expand=True), - ft.Button(text="Join chat") - ] -) +--8<-- "{{ examples }}/expand_textfield_in_row.py" ``` +{{ image(example_images + "/expand_textfield_in_row.png", alt="expand textfield in row", width="70%") }} + ### Example 2 In this example, we create a [`Row`][flet.Row] with three [`Container`][flet.Container]s, distributed like 20% / 60% / 20%: ```python -ft.Row( - controls=[ - ft.Container(content=ft.Text("A"), expand=1), - ft.Container(content=ft.Text("B"), expand=3), - ft.Container(content=ft.Text("C"), expand=1) - ] -) +--8<-- "{{ examples }}/expand_row_proportional_1_3_1.py" ``` Here, the available space is split into 5 total parts (1+3+1). The first and third containers get 1 part each (20%), and the middle one gets 3 parts (60%). +{{ image(example_images + "/expand_row_proportional_1_3_1.png", alt="expand row proportional 1 3 1", width="70%") }} + ### Example 3 -This example demonstrates how two [`Card`][flet.Card] controls inside a [`Row`][flet.Row] can +This example demonstrates how two controls inside a [`Row`][flet.Row] can each expand to fill half of the available horizontal space using expand=True. -The layout uses a full-screen [`Column`][flet.Column] and a nested Row, where both cards are -expanded equally — resulting in a 50/50 split. +The layout uses a parent [`Container`][flet.Container] and a nested row, where both controls are +expanded equally, resulting in a 50/50 split. ```python -import flet as ft - -def main(page: ft.Page): - page.spacing = 0 - page.padding = 0 - - page.add( - ft.Column( - expand=True, - spacing=0, - controls=[ - ft.Row( - expand=True, - spacing=0, - controls=[ - ft.Card( - content=ft.Text("Card_1"), - color=ft.Colors.ORANGE_300, - expand=True, - height=page.height, - margin=0, - ), - ft.Card( - content=ft.Text("Card_2"), - color=ft.Colors.GREEN_100, - expand=True, - height=page.height, - margin=0, - ), - ], - ), - ], - ), - ) - -ft.run(main) +--8<-- "{{ examples }}/expand_row_equal_split.py" ``` +{{ image(example_images + "/expand_row_equal_split.png", alt="expand row equal split", width="70%") }} + ## `expand_loose` The [`expand_loose`][flet.Control.expand_loose] property allows a control to *optionally* expand @@ -115,82 +81,7 @@ or any of their subclasses: [`Row`][flet.Row], [`Column`][flet.Column], [`View`] In this example, [`Container`][flet.Container]s being placed in [`Row`][flet.Row]s with `expand_loose = True`: ```python -import flet as ft - - -class Message(ft.Container): - def __init__(self, author, body): - super().__init__() - self.content = ft.Column( - controls=[ - ft.Text(author, weight=ft.FontWeight.BOLD), - ft.Text(body), - ], - ) - self.border = ft.border.all(1, ft.Colors.BLACK) - self.border_radius = ft.border_radius.all(10) - self.bgcolor = ft.Colors.GREEN_200 - self.padding = 10 - self.expand = True - self.expand_loose = True - - -def main(page: ft.Page): - page.window.width = 393 - page.window.height = 600 - page.window.always_on_top = False - - 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(chat) - - -ft.run(main) +--8<-- "{{ examples }}/expand_loose_chat_messages.py" ``` -{{ image("../assets/controls/control/expand-loose.png", alt="expand_loose", width="65%") }} +{{ image(example_images + "/expand_loose_chat_messages.png", alt="expand loose chat messages", width="70%") }} diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/badge.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/badge.png new file mode 100644 index 0000000000..1f5b121b77 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/badge.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/col.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/col.png new file mode 100644 index 0000000000..8f6c1ba707 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/col.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/disabled_propagates_to_children.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/disabled_propagates_to_children.png new file mode 100644 index 0000000000..4abac211aa Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/disabled_propagates_to_children.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/opacity.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/opacity.png new file mode 100644 index 0000000000..e609bd2719 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/opacity.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/parent_not_visible_child_visible.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/parent_not_visible_child_visible.png new file mode 100644 index 0000000000..557ec60930 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/parent_not_visible_child_visible.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/rtl.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/rtl.png new file mode 100644 index 0000000000..7c9208ac77 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/rtl.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/visible.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/visible.png new file mode 100644 index 0000000000..41d814e99f Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control/visible.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control_isolated/tooltip_custom_properties_on_hover.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control_isolated/tooltip_custom_properties_on_hover.png new file mode 100644 index 0000000000..d53718772c Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control_isolated/tooltip_custom_properties_on_hover.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control_isolated/tooltip_shows_on_hover.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control_isolated/tooltip_shows_on_hover.png new file mode 100644 index 0000000000..c8017d2381 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/control_isolated/tooltip_shows_on_hover.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/align_inside_container.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/align_inside_container.png new file mode 100644 index 0000000000..e2e8ee131c Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/align_inside_container.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/align_inside_stack.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/align_inside_stack.png new file mode 100644 index 0000000000..1a1d90c14c Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/align_inside_stack.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/animate_position.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/animate_position.png new file mode 100644 index 0000000000..5a5f7af442 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/animate_position.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/aspect_ratio.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/aspect_ratio.png new file mode 100644 index 0000000000..51cac3511c Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/aspect_ratio.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/flip.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/flip.png index cc57081482..ad55d18415 100644 Binary files a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/flip.png and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/flip.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/margin_around.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/margin_around.png new file mode 100644 index 0000000000..815832a3b6 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/margin_around.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/margin_bottom_right.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/margin_bottom_right.png new file mode 100644 index 0000000000..a4bb8d5559 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/margin_bottom_right.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/matrix4_mirrored_spin.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/matrix4_mirrored_spin.png index b6991eddba..75e7a181c5 100644 Binary files a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/matrix4_mirrored_spin.png and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/matrix4_mirrored_spin.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/matrix4_multiply_chain.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/matrix4_multiply_chain.png index a4cc19964d..f7d53ef9d8 100644 Binary files a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/matrix4_multiply_chain.png and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/matrix4_multiply_chain.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/matrix4_perspective_tilt.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/matrix4_perspective_tilt.png index 498b9c5136..cb7dea745d 100644 Binary files a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/matrix4_perspective_tilt.png and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/matrix4_perspective_tilt.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/matrix4_skew_and_rotate.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/matrix4_skew_and_rotate.png index 0e41dafa88..24715edfad 100644 Binary files a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/matrix4_skew_and_rotate.png and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/matrix4_skew_and_rotate.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/offset.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/offset.png index 8b7e2c4d90..18d312e431 100644 Binary files a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/offset.png and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/offset.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/position_constraint_combinations.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/position_constraint_combinations.png new file mode 100644 index 0000000000..a7c9374138 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/position_constraint_combinations.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/position_right_bottom.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/position_right_bottom.png new file mode 100644 index 0000000000..9e24f8869c Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/position_right_bottom.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/rotate.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/rotate.png index 9a4ad12db5..4e91bd6d5f 100644 Binary files a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/rotate.png and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/rotate.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/rotate_numeric_value.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/rotate_numeric_value.png new file mode 100644 index 0000000000..b139476417 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/rotate_numeric_value.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/scale.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/scale.png index b588bd9c2e..8745d43792 100644 Binary files a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/scale.png and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/scale.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/scale_numeric_value.png b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/scale_numeric_value.png new file mode 100644 index 0000000000..9d5bd433ab Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/core/golden/macos/layout_control/scale_numeric_value.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/core/test_control.py b/sdk/python/packages/flet/integration_tests/controls/core/test_control.py new file mode 100644 index 0000000000..98c7f26af8 --- /dev/null +++ b/sdk/python/packages/flet/integration_tests/controls/core/test_control.py @@ -0,0 +1,277 @@ +import pytest + +import flet as ft +import flet.testing as ftt + + +@pytest.mark.asyncio(loop_scope="module") +async def test_visible(flet_app: ftt.FletTestApp, request): + flet_app.page.theme_mode = ft.ThemeMode.LIGHT + await flet_app.assert_control_screenshot( + request.node.name, + ft.Column( + spacing=8, + controls=[ + ft.Container( + width=260, + height=44, + bgcolor=ft.Colors.GREEN_300, + border_radius=8, + alignment=ft.Alignment.CENTER, + content=ft.Text("Visible: True"), + ), + ft.Container( + width=260, + height=44, + bgcolor=ft.Colors.RED_300, + border_radius=8, + alignment=ft.Alignment.CENTER, + content=ft.Text("Visible: False"), + visible=False, + ), + ], + ), + ) + + +@pytest.mark.asyncio(loop_scope="module") +async def test_parent_not_visible_child_visible(flet_app: ftt.FletTestApp, request): + flet_app.page.theme_mode = ft.ThemeMode.LIGHT + await flet_app.assert_control_screenshot( + request.node.name, + ft.Column( + spacing=8, + controls=[ + ft.Container( + width=260, + height=44, + bgcolor=ft.Colors.GREEN_300, + border_radius=8, + alignment=ft.Alignment.CENTER, + content=ft.Text("Visible sibling"), + ), + ft.Container( + visible=False, + content=ft.Container( + visible=True, + width=260, + height=44, + bgcolor=ft.Colors.RED_300, + border_radius=8, + alignment=ft.Alignment.CENTER, + content=ft.Text("Hidden parent, visible child"), + ), + ), + ], + ), + ) + + +@pytest.mark.asyncio(loop_scope="module") +async def test_disabled_propagates_to_children(flet_app: ftt.FletTestApp, request): + flet_app.page.theme_mode = ft.ThemeMode.LIGHT + await flet_app.assert_control_screenshot( + request.node.name, + ft.Column( + spacing=12, + controls=[ + ft.Column( + spacing=8, + controls=[ + ft.Text("Enabled parent"), + ft.TextField(label="Name", value="John"), + ft.Button("Save"), + ], + ), + ft.Column( + disabled=True, + spacing=8, + controls=[ + ft.Text("Disabled parent"), + ft.TextField(label="Name", value="John"), + ft.Button("Save"), + ], + ), + ], + ), + ) + + +@pytest.mark.asyncio(loop_scope="module") +async def test_badge(flet_app: ftt.FletTestApp, request): + flet_app.page.theme_mode = ft.ThemeMode.LIGHT + await flet_app.assert_control_screenshot( + request.node.name, + ft.Container( + padding=10, + content=ft.Row( + spacing=20, + controls=[ + ft.Container( + padding=10, + content=ft.FilledIconButton( + icon=ft.Icons.NOTIFICATIONS_OUTLINED, + badge="3", + ), + ), + ft.Container( + padding=10, + content=ft.FilledIconButton( + icon=ft.Icons.MAIL_OUTLINED, + badge=ft.Badge( + label="99+", + bgcolor=ft.Colors.RED_400, + text_color=ft.Colors.WHITE, + alignment=ft.Alignment(1, -1), + offset=ft.Offset(-2, 2), + ), + ), + ), + ], + ), + ), + ) + + +@pytest.mark.asyncio(loop_scope="module") +async def test_opacity(flet_app: ftt.FletTestApp, request): + flet_app.page.theme_mode = ft.ThemeMode.LIGHT + await flet_app.assert_control_screenshot( + request.node.name, + ft.Column( + spacing=10, + controls=[ + ft.Container( + width=260, + height=46, + bgcolor=ft.Colors.BLUE_300, + alignment=ft.Alignment.CENTER, + border_radius=8, + content=ft.Text("opacity=1.0"), + opacity=1.0, + ), + ft.Container( + width=260, + height=46, + bgcolor=ft.Colors.BLUE_300, + alignment=ft.Alignment.CENTER, + border_radius=8, + content=ft.Text("opacity=0.6"), + opacity=0.6, + ), + ft.Container( + width=260, + height=46, + bgcolor=ft.Colors.BLUE_300, + alignment=ft.Alignment.CENTER, + border_radius=8, + content=ft.Text("opacity=0.25"), + opacity=0.25, + ), + ], + ), + ) + + +@pytest.mark.asyncio(loop_scope="module") +async def test_rtl(flet_app: ftt.FletTestApp, request): + flet_app.page.theme_mode = ft.ThemeMode.LIGHT + await flet_app.assert_control_screenshot( + request.node.name, + ft.Column( + spacing=12, + controls=[ + ft.Container( + width=320, + border=ft.Border.all(1, ft.Colors.BLUE_GREY_200), + padding=10, + content=ft.Row( + alignment=ft.MainAxisAlignment.START, + controls=[ + ft.Container( + width=44, height=30, bgcolor=ft.Colors.RED_300 + ), + ft.Container( + width=44, height=30, bgcolor=ft.Colors.GREEN_300 + ), + ft.Container( + width=44, height=30, bgcolor=ft.Colors.BLUE_300 + ), + ], + ), + ), + ft.Container( + width=320, + border=ft.Border.all(1, ft.Colors.BLUE_GREY_200), + padding=10, + content=ft.Row( + rtl=True, + alignment=ft.MainAxisAlignment.START, + controls=[ + ft.Container( + width=44, height=30, bgcolor=ft.Colors.RED_300 + ), + ft.Container( + width=44, height=30, bgcolor=ft.Colors.GREEN_300 + ), + ft.Container( + width=44, height=30, bgcolor=ft.Colors.BLUE_300 + ), + ], + ), + ), + ], + ), + ) + + +@pytest.mark.asyncio(loop_scope="module") +async def test_col(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=520, + padding=10, + border=ft.Border.all(1, ft.Colors.BLUE_GREY_200), + border_radius=8, + content=ft.ResponsiveRow( + run_spacing=8, + spacing=8, + controls=[ + ft.Container( + col=6, + height=52, + bgcolor=ft.Colors.CYAN_300, + border_radius=8, + alignment=ft.Alignment.CENTER, + content=ft.Text("col=6"), + ), + ft.Container( + col=6, + height=52, + bgcolor=ft.Colors.AMBER_300, + border_radius=8, + alignment=ft.Alignment.CENTER, + content=ft.Text("col=6"), + ), + ft.Container( + col={"sm": 4, "md": 3}, + height=52, + bgcolor=ft.Colors.PINK_200, + border_radius=8, + alignment=ft.Alignment.CENTER, + content=ft.Text('col={"sm":4,"md":3}'), + ), + ft.Container( + col={"sm": 8, "md": 9}, + height=52, + bgcolor=ft.Colors.GREEN_200, + border_radius=8, + alignment=ft.Alignment.CENTER, + content=ft.Text('col={"sm":8,"md":9}'), + ), + ], + ), + ), + ) diff --git a/sdk/python/packages/flet/integration_tests/controls/core/test_control_isolated.py b/sdk/python/packages/flet/integration_tests/controls/core/test_control_isolated.py new file mode 100644 index 0000000000..cc26700c3d --- /dev/null +++ b/sdk/python/packages/flet/integration_tests/controls/core/test_control_isolated.py @@ -0,0 +1,100 @@ +import pytest + +import flet as ft +import flet.testing as ftt + + +@pytest.mark.asyncio(loop_scope="function") +async def test_tooltip_string_is_findable(flet_app_function: ftt.FletTestApp): + flet_app_function.page.add( + ft.IconButton( + icon=ft.Icons.INFO_OUTLINED, + tooltip="Info tooltip", + ) + ) + await flet_app_function.tester.pump_and_settle() + + finder = await flet_app_function.tester.find_by_tooltip("Info tooltip") + assert finder.count == 1 + + +@pytest.mark.asyncio(loop_scope="function") +async def test_tooltip_shows_on_hover(flet_app_function: ftt.FletTestApp, request): + flet_app_function.page.theme_mode = ft.ThemeMode.LIGHT + flet_app_function.page.enable_screenshots = True + flet_app_function.resize_page(420, 300) + flet_app_function.page.update() + + flet_app_function.page.add( + ft.Container( + padding=100, + content=ft.IconButton( + key="info_btn", + icon=ft.Icons.INFO_OUTLINED, + tooltip=ft.Tooltip(message="Tooltip message"), + ), + ) + ) + flet_app_function.page.update() + await flet_app_function.tester.pump_and_settle() + + button = await flet_app_function.tester.find_by_key("info_btn") + await flet_app_function.tester.mouse_hover(button) + await flet_app_function.tester.pump_and_settle() + + flet_app_function.assert_screenshot( + request.node.name, + await flet_app_function.page.take_screenshot( + pixel_ratio=flet_app_function.screenshots_pixel_ratio + ), + ) + + +@pytest.mark.asyncio(loop_scope="function") +async def test_tooltip_custom_properties_on_hover( + flet_app_function: ftt.FletTestApp, request +): + flet_app_function.page.theme_mode = ft.ThemeMode.LIGHT + flet_app_function.page.enable_screenshots = True + flet_app_function.resize_page(460, 320) + flet_app_function.page.update() + + flet_app_function.page.add( + ft.Container( + padding=100, + content=ft.IconButton( + key="info_btn_custom", + icon=ft.Icons.HELP_OUTLINE, + tooltip=ft.Tooltip( + message="Customized tooltip for Control.tooltip", + wait_duration=0, + show_duration=5000, + prefer_below=True, + vertical_offset=20, + bgcolor=ft.Colors.BLUE_GREY_900, + text_style=ft.TextStyle( + color=ft.Colors.WHITE, weight=ft.FontWeight.W_600, size=14 + ), + padding=ft.Padding.symmetric(horizontal=14, vertical=10), + margin=ft.Margin.only(top=8, left=8, right=8), + text_align=ft.TextAlign.CENTER, + decoration=ft.BoxDecoration( + border_radius=ft.BorderRadius.all(10), + ), + ), + ), + ) + ) + flet_app_function.page.update() + await flet_app_function.tester.pump_and_settle() + + button = await flet_app_function.tester.find_by_key("info_btn_custom") + await flet_app_function.tester.mouse_hover(button) + await flet_app_function.tester.pump_and_settle() + + flet_app_function.assert_screenshot( + request.node.name, + await flet_app_function.page.take_screenshot( + pixel_ratio=flet_app_function.screenshots_pixel_ratio + ), + ) diff --git a/sdk/python/packages/flet/integration_tests/controls/core/test_layout_control.py b/sdk/python/packages/flet/integration_tests/controls/core/test_layout_control.py index ccda1fdab7..aa46fa824c 100644 --- a/sdk/python/packages/flet/integration_tests/controls/core/test_layout_control.py +++ b/sdk/python/packages/flet/integration_tests/controls/core/test_layout_control.py @@ -6,6 +6,216 @@ import flet.testing as ftt +@pytest.mark.asyncio(loop_scope="module") +async def test_align_inside_stack(flet_app: ftt.FletTestApp, request): + flet_app.page.theme_mode = ft.ThemeMode.LIGHT + await flet_app.assert_control_screenshot( + request.node.name, + ft.Stack( + [ + ft.Button("A", align=ft.Alignment(0, 0)), + ft.Button("B", align=ft.Alignment(0.9, 0.9)), + ft.Button("C", align=ft.Alignment.BOTTOM_LEFT), + ], + width=200, + height=200, + ), + ) + + +@pytest.mark.asyncio(loop_scope="module") +async def test_align_inside_container(flet_app: ftt.FletTestApp, request): + flet_app.page.theme_mode = ft.ThemeMode.LIGHT + await flet_app.assert_control_screenshot( + request.node.name, + ft.Container( + ft.Button("B", align=ft.Alignment(0.9, 0.9)), + width=200, + height=200, + ), + ) + + +@pytest.mark.asyncio(loop_scope="module") +async def test_margin_around(flet_app: ftt.FletTestApp, request): + flet_app.page.theme_mode = ft.ThemeMode.LIGHT + await flet_app.assert_control_screenshot( + request.node.name, + ft.Button( + "Button with margin", + margin=ft.Margin.all(20), + ), + ) + + +@pytest.mark.asyncio(loop_scope="module") +async def test_margin_bottom_right(flet_app: ftt.FletTestApp, request): + flet_app.page.theme_mode = ft.ThemeMode.LIGHT + await flet_app.assert_control_screenshot( + request.node.name, + ft.Button( + "Button with margin", + margin=ft.Margin.only(bottom=20, right=20), + ), + ) + + +@pytest.mark.asyncio(loop_scope="module") +async def test_position_right_bottom(flet_app: ftt.FletTestApp, request): + await flet_app.assert_control_screenshot( + request.node.name, + ft.Stack( + width=420, + height=240, + controls=[ + ft.Container( + border=ft.Border.all(2, ft.Colors.BLUE_GREY_200), + border_radius=12, + ), + ft.Container( + width=120, + height=70, + right=24, + bottom=20, + border_radius=12, + bgcolor=ft.Colors.CYAN_300, + alignment=ft.Alignment.CENTER, + content=ft.Text("right+bottom", size=14, weight=ft.FontWeight.BOLD), + ), + ], + ), + ) + + +@pytest.mark.asyncio(loop_scope="module") +async def test_position_constraint_combinations(flet_app: ftt.FletTestApp, request): + await flet_app.assert_control_screenshot( + request.node.name, + ft.Stack( + width=440, + height=280, + controls=[ + ft.Container( + border=ft.Border.all(2, ft.Colors.BLUE_GREY_200), + border_radius=12, + ), + ft.Container( + left=40, + right=40, + top=42, + height=62, + border_radius=10, + bgcolor=ft.Colors.AMBER_300, + alignment=ft.Alignment.CENTER, + content=ft.Text("left + right", weight=ft.FontWeight.BOLD), + ), + ft.Container( + top=118, + bottom=38, + left=164, + width=110, + border_radius=10, + bgcolor=ft.Colors.GREEN_300, + alignment=ft.Alignment.CENTER, + content=ft.Text("top + bottom", weight=ft.FontWeight.BOLD), + ), + ], + ), + ) + + +@pytest.mark.asyncio(loop_scope="module") +async def test_animate_position(flet_app: ftt.FletTestApp, request): + # TODO(Feodor): Test framework needs improvement to correctly capture + # multiple animation screenshots. + animated = ft.Container( + width=90, + height=64, + left=18, + top=24, + border_radius=10, + bgcolor=ft.Colors.CYAN_300, + alignment=ft.Alignment.CENTER, + content=ft.Text("move", weight=ft.FontWeight.BOLD), + animate_position=ft.Animation(600, ft.AnimationCurve.EASE_IN_OUT), + ) + root = ft.Stack( + width=320, + height=180, + controls=[ + ft.Container( + border=ft.Border.all(2, ft.Colors.BLUE_GREY_200), + border_radius=12, + ), + animated, + ], + ) + screenshot = ft.Screenshot(root) + + flet_app.page.clean() + flet_app.page.add(screenshot) + await flet_app.tester.pump_and_settle() + + animated.left = 196 + animated.top = 98 + animated.update() + await flet_app.tester.pump_and_settle() + + flet_app.assert_screenshot( + request.node.name, + await screenshot.capture(pixel_ratio=flet_app.screenshots_pixel_ratio), + ) + + +@pytest.mark.asyncio(loop_scope="module") +async def test_aspect_ratio(flet_app: ftt.FletTestApp, request): + await flet_app.assert_control_screenshot( + request.node.name, + ft.Container( + width=100, + height=280, + border=ft.Border.all(2, ft.Colors.BLUE_GREY_200), + border_radius=12, + alignment=ft.Alignment.TOP_LEFT, + padding=12, + content=ft.Column( + spacing=12, + controls=[ + ft.Container( + aspect_ratio=2, + border_radius=12, + bgcolor=ft.Colors.CYAN_300, + alignment=ft.Alignment.CENTER, + content=ft.Text( + "ratio 2.0", weight=ft.FontWeight.BOLD, size=13 + ), + ), + ft.Container( + aspect_ratio=0.5, + border_radius=12, + bgcolor=ft.Colors.ORANGE_300, + alignment=ft.Alignment.CENTER, + content=ft.Text( + "ratio 0.5", weight=ft.FontWeight.BOLD, size=13 + ), + ), + ft.Container( + width=50, + height=50, + aspect_ratio=3, + border_radius=12, + bgcolor=ft.Colors.PINK_200, + alignment=ft.Alignment.CENTER, + content=ft.Text( + "ratio wins", weight=ft.FontWeight.BOLD, size=13 + ), + ), + ], + ), + ), + ) + + @pytest.mark.asyncio(loop_scope="module") async def test_flip(flet_app: ftt.FletTestApp, request): await flet_app.assert_control_screenshot( @@ -46,6 +256,22 @@ async def test_rotate(flet_app: ftt.FletTestApp, request): ) +@pytest.mark.asyncio(loop_scope="module") +async def test_rotate_numeric_value(flet_app: ftt.FletTestApp, request): + await flet_app.assert_control_screenshot( + request.node.name, + ft.Container( + width=220, + height=120, + bgcolor=ft.Colors.BLUE_300, + border_radius=16, + alignment=ft.Alignment.CENTER, + content=ft.Text("Rotate", size=28, weight=ft.FontWeight.BOLD), + rotate=pi / 10, + ), + ) + + @pytest.mark.asyncio(loop_scope="module") async def test_scale(flet_app: ftt.FletTestApp, request): await flet_app.assert_control_screenshot( @@ -67,6 +293,22 @@ async def test_scale(flet_app: ftt.FletTestApp, request): ) +@pytest.mark.asyncio(loop_scope="module") +async def test_scale_numeric_value(flet_app: ftt.FletTestApp, request): + await flet_app.assert_control_screenshot( + request.node.name, + ft.Container( + width=220, + height=120, + bgcolor=ft.Colors.GREEN_300, + border_radius=16, + alignment=ft.Alignment.CENTER, + content=ft.Text("Scale", size=28, weight=ft.FontWeight.BOLD), + scale=2.5, + ), + ) + + @pytest.mark.asyncio(loop_scope="module") async def test_offset(flet_app: ftt.FletTestApp, request): await flet_app.assert_control_screenshot( diff --git a/sdk/python/packages/flet/integration_tests/controls/types/test_align.py b/sdk/python/packages/flet/integration_tests/controls/types/test_align.py deleted file mode 100644 index 5a43cf91b4..0000000000 --- a/sdk/python/packages/flet/integration_tests/controls/types/test_align.py +++ /dev/null @@ -1,34 +0,0 @@ -import pytest - -import flet as ft -import flet.testing as ftt - - -@pytest.mark.asyncio(loop_scope="module") -async def test_align_inside_stack(flet_app: ftt.FletTestApp, request): - flet_app.page.theme_mode = ft.ThemeMode.LIGHT - await flet_app.assert_control_screenshot( - request.node.name, - ft.Stack( - [ - ft.Button("A", align=ft.Alignment(0, 0)), - ft.Button("B", align=ft.Alignment(0.9, 0.9)), - ft.Button("C", align=ft.Alignment.BOTTOM_LEFT), - ], - width=200, - height=200, - ), - ) - - -@pytest.mark.asyncio(loop_scope="module") -async def test_align_inside_container(flet_app: ftt.FletTestApp, request): - flet_app.page.theme_mode = ft.ThemeMode.LIGHT - await flet_app.assert_control_screenshot( - request.node.name, - ft.Container( - ft.Button("B", align=ft.Alignment(0.9, 0.9)), - width=200, - height=200, - ), - ) diff --git a/sdk/python/packages/flet/integration_tests/controls/types/test_margin.py b/sdk/python/packages/flet/integration_tests/controls/types/test_margin.py deleted file mode 100644 index 2183fdc5d2..0000000000 --- a/sdk/python/packages/flet/integration_tests/controls/types/test_margin.py +++ /dev/null @@ -1,28 +0,0 @@ -import pytest - -import flet as ft -import flet.testing as ftt - - -@pytest.mark.asyncio(loop_scope="module") -async def test_margin_around(flet_app: ftt.FletTestApp, request): - flet_app.page.theme_mode = ft.ThemeMode.LIGHT - await flet_app.assert_control_screenshot( - request.node.name, - ft.Button( - "Button with margin", - margin=ft.Margin.all(20), - ), - ) - - -@pytest.mark.asyncio(loop_scope="module") -async def test_margin_bottom_right(flet_app: ftt.FletTestApp, request): - flet_app.page.theme_mode = ft.ThemeMode.LIGHT - await flet_app.assert_control_screenshot( - request.node.name, - ft.Button( - "Button with margin", - margin=ft.Margin.only(bottom=20, right=20), - ), - ) diff --git a/sdk/python/packages/flet/integration_tests/examples/core/golden/macos/control/expand_loose_chat_messages.png b/sdk/python/packages/flet/integration_tests/examples/core/golden/macos/control/expand_loose_chat_messages.png new file mode 100644 index 0000000000..510d545471 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/examples/core/golden/macos/control/expand_loose_chat_messages.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/core/golden/macos/control/expand_row_equal_split.png b/sdk/python/packages/flet/integration_tests/examples/core/golden/macos/control/expand_row_equal_split.png new file mode 100644 index 0000000000..78bff7ee0e Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/examples/core/golden/macos/control/expand_row_equal_split.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/core/golden/macos/control/expand_row_proportional_1_3_1.png b/sdk/python/packages/flet/integration_tests/examples/core/golden/macos/control/expand_row_proportional_1_3_1.png new file mode 100644 index 0000000000..d09a46dc4f Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/examples/core/golden/macos/control/expand_row_proportional_1_3_1.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/core/golden/macos/control/expand_textfield_in_row.png b/sdk/python/packages/flet/integration_tests/examples/core/golden/macos/control/expand_textfield_in_row.png new file mode 100644 index 0000000000..123b94d126 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/examples/core/golden/macos/control/expand_textfield_in_row.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/core/test_control.py b/sdk/python/packages/flet/integration_tests/examples/core/test_control.py new file mode 100644 index 0000000000..17b51ddeb4 --- /dev/null +++ b/sdk/python/packages/flet/integration_tests/examples/core/test_control.py @@ -0,0 +1,61 @@ +import pytest + +import flet.testing as ftt +from examples.controls.control import ( + expand_loose_chat_messages, + expand_row_equal_split, + expand_row_proportional_1_3_1, + expand_textfield_in_row, +) + + +@pytest.mark.parametrize( + "flet_app_function", + [{"flet_app_main": expand_textfield_in_row.main}], + indirect=True, +) +@pytest.mark.asyncio(loop_scope="function") +async def test_expand_textfield_in_row(flet_app_function: ftt.FletTestApp): + flet_app_function.assert_screenshot( + "expand_textfield_in_row", + await flet_app_function.take_page_controls_screenshot(), + ) + + +@pytest.mark.parametrize( + "flet_app_function", + [{"flet_app_main": expand_row_proportional_1_3_1.main}], + indirect=True, +) +@pytest.mark.asyncio(loop_scope="function") +async def test_expand_row_proportional_1_3_1(flet_app_function: ftt.FletTestApp): + flet_app_function.assert_screenshot( + "expand_row_proportional_1_3_1", + await flet_app_function.take_page_controls_screenshot(), + ) + + +@pytest.mark.parametrize( + "flet_app_function", + [{"flet_app_main": expand_row_equal_split.main}], + indirect=True, +) +@pytest.mark.asyncio(loop_scope="function") +async def test_expand_row_equal_split(flet_app_function: ftt.FletTestApp): + flet_app_function.assert_screenshot( + "expand_row_equal_split", + await flet_app_function.take_page_controls_screenshot(), + ) + + +@pytest.mark.parametrize( + "flet_app_function", + [{"flet_app_main": expand_loose_chat_messages.main}], + indirect=True, +) +@pytest.mark.asyncio(loop_scope="function") +async def test_expand_loose_chat_messages(flet_app_function: ftt.FletTestApp): + flet_app_function.assert_screenshot( + "expand_loose_chat_messages", + await flet_app_function.take_page_controls_screenshot(), + ) diff --git a/sdk/python/packages/flet/src/flet/controls/layout_control.py b/sdk/python/packages/flet/src/flet/controls/layout_control.py index 977ec3b88f..457b15eb8b 100644 --- a/sdk/python/packages/flet/src/flet/controls/layout_control.py +++ b/sdk/python/packages/flet/src/flet/controls/layout_control.py @@ -222,7 +222,11 @@ def main(page: ft.Page): aspect_ratio: Optional[Number] = None """ The aspect ratio of the control. - It is defined as the ratio of the width to the height. + It is defined as the ratio of [`width`][(c).] to [`height`][(c).]. + + Note: + In current implementation, if [`aspect_ratio`][(c).] is set, [`width`][(c).] + and [`height`][(c).] on the same control are ignored for final rendered size. """ animate_opacity: Optional[AnimationValue] = None