Skip to content

Commit 7ae1bff

Browse files
committed
Merge branch 'main' into v1-icons-codepoint
2 parents 53a33f5 + 363881e commit 7ae1bff

File tree

97 files changed

+1115
-19
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+1115
-19
lines changed

.appveyor.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ environment:
2626
job_group: build_flet_package
2727
APPVEYOR_BUILD_WORKER_IMAGE: ubuntu2204
2828

29-
- job_name: Integration tests on macOS
30-
job_group: integration_tests
31-
python_stack: python 3.10
32-
APPVEYOR_BUILD_WORKER_IMAGE: macos-sonoma
33-
FLET_TEST_SCREENSHOTS_PIXEL_RATIO: 2.0
34-
FLET_TEST_SCREENSHOTS_SIMILARITY_THRESHOLD: 99.0
35-
FLET_TEST_DISABLE_FVM: 1
29+
# - job_name: Integration tests on macOS
30+
# job_group: integration_tests
31+
# python_stack: python 3.10
32+
# APPVEYOR_BUILD_WORKER_IMAGE: macos-sonoma
33+
# FLET_TEST_SCREENSHOTS_PIXEL_RATIO: 2.0
34+
# FLET_TEST_SCREENSHOTS_SIMILARITY_THRESHOLD: 99.0
35+
# FLET_TEST_DISABLE_FVM: 1
3636

3737
- job_name: Build Flet for Windows
3838
job_group: build_flet
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: macOS Integration Tests
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
env:
8+
FLUTTER_VERSION: "3.32.8"
9+
FLET_TEST_SCREENSHOTS_PIXEL_RATIO: "2.0"
10+
FLET_TEST_SCREENSHOTS_SIMILARITY_THRESHOLD: "99.0"
11+
FLET_TEST_DISABLE_FVM: "1"
12+
13+
jobs:
14+
test-macos:
15+
runs-on: macos-14
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Install CocoaPods
20+
run: brew install cocoapods
21+
22+
- name: Setup Flutter
23+
uses: subosito/flutter-action@v2
24+
with:
25+
flutter-version: ${{ env.FLUTTER_VERSION }}
26+
channel: stable
27+
cache: true
28+
29+
- name: Install uv
30+
shell: bash
31+
run: |
32+
curl -LsSf https://astral.sh/uv/install.sh | sh
33+
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
34+
35+
- name: Show tool versions
36+
run: |
37+
python3 --version
38+
uv --version
39+
pod --version
40+
flutter --version
41+
42+
- name: Run integration tests
43+
working-directory: sdk/python
44+
run: |
45+
uv run pytest -s -o log_cli=true -o log_cli_level=INFO packages/flet/integration_tests
46+
47+
- name: Upload failure screenshots
48+
if: failure()
49+
uses: actions/upload-artifact@v4
50+
with:
51+
name: integration-test-failures-macos
52+
path: sdk/python/packages/flet/integration_tests/**/*_actual.png
53+
if-no-files-found: ignore

client/integration_test/flutter_tester.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ class FlutterWidgetTester implements Tester {
7474
Future<void> tap(TestFinder finder) =>
7575
_tester.tap((finder as FlutterTestFinder).raw);
7676

77+
@override
78+
Future<void> longPress(TestFinder finder) =>
79+
_tester.longPress((finder as FlutterTestFinder).raw);
80+
7781
@override
7882
Future<void> enterText(TestFinder finder, String text) =>
7983
_tester.enterText((finder as FlutterTestFinder).raw, text);

packages/flet/lib/src/controls/page.dart

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import 'dart:ui' as ui;
12
import 'dart:ui';
23

34
import 'package:collection/collection.dart';
45
import 'package:flutter/cupertino.dart';
56
import 'package:flutter/foundation.dart';
67
import 'package:flutter/material.dart';
8+
import 'package:flutter/rendering.dart';
79
import 'package:flutter/services.dart';
810
import 'package:flutter_localizations/flutter_localizations.dart';
911
import 'package:provider/provider.dart';
@@ -25,6 +27,7 @@ import '../utils/platform_utils_web.dart'
2527
import '../utils/session_store_web.dart'
2628
if (dart.library.io) "../utils/session_store_non_web.dart";
2729
import '../utils/theme.dart';
30+
import '../utils/time.dart';
2831
import '../utils/user_fonts.dart';
2932
import '../widgets/animated_transition_page.dart';
3033
import '../widgets/loading_page.dart';
@@ -44,6 +47,7 @@ class PageControl extends StatefulWidget {
4447

4548
class _PageControlState extends State<PageControl> with WidgetsBindingObserver {
4649
final _navigatorKey = GlobalKey<NavigatorState>();
50+
final _rootKey = GlobalKey();
4751
late final RouteState _routeState;
4852
late final SimpleRouterDelegate _routerDelegate;
4953
late final RouteParser _routeParser;
@@ -52,7 +56,7 @@ class _PageControlState extends State<PageControl> with WidgetsBindingObserver {
5256
ServiceRegistry? _userServices;
5357
bool? _prevOnKeyboardEvent;
5458
bool _keyboardHandlerSubscribed = false;
55-
59+
double _dpr = 1.0;
5660
String? _prevViewRoutes;
5761

5862
final Map<int, MultiView> _multiViews = <int, MultiView>{};
@@ -87,13 +91,14 @@ class _PageControlState extends State<PageControl> with WidgetsBindingObserver {
8791
onRestart: () => _handleAppLifecycleTransition('restart'));
8892

8993
_attachKeyboardListenerIfNeeded();
94+
widget.control.addInvokeMethodListener(_invokeMethod);
9095
}
9196

9297
@override
9398
void didChangeDependencies() {
9499
debugPrint("Page.didChangeDependencies: ${widget.control.id}");
95100
super.didChangeDependencies();
96-
101+
_dpr = MediaQuery.devicePixelRatioOf(context);
97102
_loadFontsIfNeeded(FletBackend.of(context));
98103
}
99104

@@ -148,9 +153,30 @@ class _PageControlState extends State<PageControl> with WidgetsBindingObserver {
148153
if (_keyboardHandlerSubscribed) {
149154
HardwareKeyboard.instance.removeHandler(_handleKeyDown);
150155
}
156+
widget.control.removeInvokeMethodListener(_invokeMethod);
151157
super.dispose();
152158
}
153159

160+
Future<dynamic> _invokeMethod(String name, dynamic args) async {
161+
debugPrint("Page.$name($args)");
162+
switch (name) {
163+
case "take_screenshot":
164+
if (_rootKey.currentContext == null) {
165+
return null;
166+
}
167+
await Future.delayed(
168+
parseDuration(args["delay"], const Duration(milliseconds: 20))!);
169+
final boundary = _rootKey.currentContext!.findRenderObject()
170+
as RenderRepaintBoundary;
171+
final image = await boundary.toImage(
172+
pixelRatio: parseDouble(args["pixel_ratio"], _dpr)!);
173+
final data = await image.toByteData(format: ui.ImageByteFormat.png);
174+
return data!.buffer.asUint8List();
175+
default:
176+
throw Exception("Unknown Page method: $name");
177+
}
178+
}
179+
154180
void _updateMultiViews() {
155181
if (!widget.control.backend.multiView) {
156182
return;
@@ -383,7 +409,7 @@ class _PageControlState extends State<PageControl> with WidgetsBindingObserver {
383409
var showSemanticsDebugger =
384410
control.getBool("show_semantics_debugger", false)!;
385411

386-
var app = widgetsDesign == PageDesign.cupertino
412+
Widget? app = widgetsDesign == PageDesign.cupertino
387413
? home != null
388414
? CupertinoApp(
389415
debugShowCheckedModeBanner: false,
@@ -432,6 +458,14 @@ class _PageControlState extends State<PageControl> with WidgetsBindingObserver {
432458
supportedLocales: localeConfiguration.supportedLocales,
433459
locale: localeConfiguration.locale,
434460
);
461+
462+
if (control.getBool("enable_screenshots") == true) {
463+
app = RepaintBoundary(
464+
key: _rootKey,
465+
child: app,
466+
);
467+
}
468+
435469
return PageContext(
436470
themeMode: themeMode,
437471
brightness: brightness,

packages/flet/lib/src/controls/submenu_button.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class _SubmenuButtonControlState extends State<SubmenuButtonControl> {
8282
leadingIcon: widget.control.buildWidget("leading"),
8383
trailingIcon: widget.control.buildWidget("trailing"),
8484
menuChildren: widget.control.buildWidgets("controls"),
85-
child: widget.control.buildWidget("content"),
85+
child: widget.control.buildTextOrWidget("content"),
8686
);
8787

8888
var focusValue = widget.control.getString("focus");

packages/flet/lib/src/services/tester.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ class TesterService extends FletService {
7979
await control.backend.tester!.tap(finder);
8080
}
8181

82+
case "long_press":
83+
var finder = _finders[args["id"]];
84+
if (finder != null) {
85+
await control.backend.tester!.longPress(finder);
86+
}
87+
8288
case "enter_text":
8389
var finder = _finders[args["id"]];
8490
if (finder != null) {

packages/flet/lib/src/testing/tester.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ abstract class Tester {
1414
TestFinder findByIcon(IconData icon);
1515
Future<Uint8List> takeScreenshot(String name);
1616
Future<void> tap(TestFinder finder);
17+
Future<void> longPress(TestFinder finder);
1718
Future<void> enterText(TestFinder finder, String text);
1819
Future<void> mouseHover(TestFinder finder);
1920
void teardown();

sdk/python/examples/apps/counter/counter.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def plus_click(e):
1818
page.add(
1919
ft.Row(
2020
[
21-
ft.IconButton(ft.Icons.REMOVE, on_click=minus_click),
21+
ft.IconButton(ft.Icons.REMOVE, on_click=minus_click, key="decrement"),
2222
txt_number,
2323
ft.IconButton(ft.Icons.ADD, on_click=plus_click),
2424
],
@@ -27,4 +27,5 @@ def plus_click(e):
2727
)
2828

2929

30-
ft.run(main)
30+
if __name__ == "__main__":
31+
ft.run(main)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from pathlib import Path
2+
3+
import counter as app
4+
import flet as ft
5+
import flet.testing as ftt
6+
import pytest
7+
import pytest_asyncio
8+
9+
10+
@pytest_asyncio.fixture(scope="module")
11+
async def flet_app(request):
12+
flet_app = ftt.FletTestApp(
13+
flutter_app_dir=(Path(__file__).parent / "../../../../../client").resolve(),
14+
flet_app_main=app.main,
15+
test_path=request.fspath,
16+
)
17+
await flet_app.start()
18+
yield flet_app
19+
await flet_app.teardown()
20+
21+
22+
@pytest.mark.asyncio(loop_scope="module")
23+
async def test_app(flet_app: ftt.FletTestApp):
24+
tester = flet_app.tester
25+
await tester.pump_and_settle()
26+
zero_text = await tester.find_by_text("0")
27+
assert zero_text.count == 1
28+
29+
# tap increment button
30+
increment_btn = await tester.find_by_icon(ft.Icons.ADD)
31+
assert increment_btn.count == 1
32+
await tester.tap(increment_btn)
33+
await tester.pump_and_settle()
34+
assert (await tester.find_by_text("1")).count == 1
35+
36+
# tap decrement button
37+
decrement_button = await tester.find_by_key("decrement")
38+
assert decrement_button.count == 1
39+
await tester.tap(decrement_button)
40+
await tester.tap(decrement_button)
41+
await tester.pump_and_settle()
42+
assert (await tester.find_by_text("-1")).count == 1

sdk/python/examples/controls/app_bar/actions_and_popup_menu.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,5 @@ def handle_checked_item_click(e: ft.Event[ft.PopupMenuItem]):
3333
page.add(ft.Text("Body!"))
3434

3535

36-
ft.run(main)
36+
if __name__ == "__main__":
37+
ft.run(main)

0 commit comments

Comments
 (0)