1907 lines
62 KiB
Dart
1907 lines
62 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'dart:async';
|
|
import 'dart:ui';
|
|
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import '../foundation/leak_tracking.dart';
|
|
|
|
void main() {
|
|
tearDown(() {
|
|
LicenseRegistry.reset();
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Material3 has sentence case labels', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: ThemeData(useMaterial3: true),
|
|
builder: (BuildContext context, Widget? child) {
|
|
return MediaQuery(
|
|
// Display has a vertical hinge down the middle
|
|
data: const MediaQueryData(
|
|
size: Size(800, 600),
|
|
displayFeatures: <DisplayFeature>[
|
|
DisplayFeature(
|
|
bounds: Rect.fromLTRB(390, 0, 410, 600),
|
|
type: DisplayFeatureType.hinge,
|
|
state: DisplayFeatureState.unknown,
|
|
),
|
|
],
|
|
),
|
|
child: child!,
|
|
);
|
|
},
|
|
home: Builder(
|
|
builder: (BuildContext context) => ElevatedButton(
|
|
onPressed: () {
|
|
showAboutDialog(
|
|
context: context,
|
|
useRootNavigator: false,
|
|
applicationName: 'A',
|
|
);
|
|
},
|
|
child: const Text('Show About Dialog'),
|
|
),
|
|
),
|
|
));
|
|
|
|
// Open the dialog.
|
|
await tester.tap(find.byType(ElevatedButton));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('Close'), findsOneWidget);
|
|
expect(find.text('View licenses'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('Material2 - AboutListTile control test', (WidgetTester tester) async {
|
|
const FlutterLogo logo = FlutterLogo();
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: false),
|
|
title: 'Pirate app',
|
|
home: Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('Home'),
|
|
),
|
|
drawer: Drawer(
|
|
child: ListView(
|
|
children: const <Widget>[
|
|
AboutListTile(
|
|
applicationVersion: '0.1.2',
|
|
applicationIcon: logo,
|
|
applicationLegalese: 'I am the very model of a modern major general.',
|
|
aboutBoxChildren: <Widget>[
|
|
Text('About box'),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.text('About Pirate app'), findsNothing);
|
|
expect(find.text('0.1.2'), findsNothing);
|
|
expect(find.byWidget(logo), findsNothing);
|
|
expect(
|
|
find.text('I am the very model of a modern major general.'),
|
|
findsNothing,
|
|
);
|
|
expect(find.text('About box'), findsNothing);
|
|
|
|
await tester.tap(find.byType(IconButton));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('About Pirate app'), findsOneWidget);
|
|
expect(find.text('0.1.2'), findsNothing);
|
|
expect(find.byWidget(logo), findsNothing);
|
|
expect(
|
|
find.text('I am the very model of a modern major general.'),
|
|
findsNothing,
|
|
);
|
|
expect(find.text('About box'), findsNothing);
|
|
|
|
await tester.tap(find.text('About Pirate app'));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('About Pirate app'), findsOneWidget);
|
|
expect(find.text('0.1.2'), findsOneWidget);
|
|
expect(find.byWidget(logo), findsOneWidget);
|
|
expect(
|
|
find.text('I am the very model of a modern major general.'),
|
|
findsOneWidget,
|
|
);
|
|
expect(find.text('About box'), findsOneWidget);
|
|
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(<String>['Pirate package '], 'Pirate license'),
|
|
]);
|
|
});
|
|
|
|
await tester.tap(find.text('VIEW LICENSES'));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('Pirate app'), findsOneWidget);
|
|
expect(find.text('0.1.2'), findsOneWidget);
|
|
expect(find.byWidget(logo), findsOneWidget);
|
|
expect(
|
|
find.text('I am the very model of a modern major general.'),
|
|
findsOneWidget,
|
|
);
|
|
await tester.tap(find.text('Pirate package '));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('Pirate license'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('Material3 - AboutListTile control test', (WidgetTester tester) async {
|
|
const FlutterLogo logo = FlutterLogo();
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: true),
|
|
title: 'Pirate app',
|
|
home: Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('Home'),
|
|
),
|
|
drawer: Drawer(
|
|
child: ListView(
|
|
children: const <Widget>[
|
|
AboutListTile(
|
|
applicationVersion: '0.1.2',
|
|
applicationIcon: logo,
|
|
applicationLegalese: 'I am the very model of a modern major general.',
|
|
aboutBoxChildren: <Widget>[
|
|
Text('About box'),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.text('About Pirate app'), findsNothing);
|
|
expect(find.text('0.1.2'), findsNothing);
|
|
expect(find.byWidget(logo), findsNothing);
|
|
expect(
|
|
find.text('I am the very model of a modern major general.'),
|
|
findsNothing,
|
|
);
|
|
expect(find.text('About box'), findsNothing);
|
|
|
|
await tester.tap(find.byType(IconButton));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('About Pirate app'), findsOneWidget);
|
|
expect(find.text('0.1.2'), findsNothing);
|
|
expect(find.byWidget(logo), findsNothing);
|
|
expect(
|
|
find.text('I am the very model of a modern major general.'),
|
|
findsNothing,
|
|
);
|
|
expect(find.text('About box'), findsNothing);
|
|
|
|
await tester.tap(find.text('About Pirate app'));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('About Pirate app'), findsOneWidget);
|
|
expect(find.text('0.1.2'), findsOneWidget);
|
|
expect(find.byWidget(logo), findsOneWidget);
|
|
expect(
|
|
find.text('I am the very model of a modern major general.'),
|
|
findsOneWidget,
|
|
);
|
|
expect(find.text('About box'), findsOneWidget);
|
|
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(<String>['Pirate package '], 'Pirate license'),
|
|
]);
|
|
});
|
|
|
|
await tester.tap(find.text('View licenses'));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('Pirate app'), findsOneWidget);
|
|
expect(find.text('0.1.2'), findsOneWidget);
|
|
expect(find.byWidget(logo), findsOneWidget);
|
|
expect(
|
|
find.text('I am the very model of a modern major general.'),
|
|
findsOneWidget,
|
|
);
|
|
await tester.tap(find.text('Pirate package '));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('Pirate license'), findsOneWidget);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('About box logic defaults to executable name for app name', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
title: 'flutter_tester',
|
|
home: Material(child: AboutListTile()),
|
|
),
|
|
);
|
|
expect(find.text('About flutter_tester'), findsOneWidget);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('LicensePage control test', (WidgetTester tester) async {
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(<String>['AAA'], 'BBB'),
|
|
]);
|
|
});
|
|
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(
|
|
<String>['Another package'],
|
|
'Another license',
|
|
),
|
|
]);
|
|
});
|
|
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: Center(
|
|
child: LicensePage(),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.text('AAA'), findsNothing);
|
|
expect(find.text('BBB'), findsNothing);
|
|
expect(find.text('Another package'), findsNothing);
|
|
expect(find.text('Another license'), findsNothing);
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
// Check for packages.
|
|
expect(find.text('AAA'), findsOneWidget);
|
|
expect(find.text('Another package'), findsOneWidget);
|
|
|
|
// Check license is displayed after entering into license page for 'AAA'.
|
|
await tester.tap(find.text('AAA'));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('BBB'), findsOneWidget);
|
|
|
|
/// Go back to list of packages.
|
|
await tester.pageBack();
|
|
await tester.pumpAndSettle();
|
|
|
|
/// Check license is displayed after entering into license page for
|
|
/// 'Another package'.
|
|
await tester.tap(find.text('Another package'));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('Another license'), findsOneWidget);
|
|
},
|
|
leakTrackingTestConfig: const LeakTrackingTestConfig(
|
|
// TODO(polina-c): remove after fixing
|
|
// https://github.com/flutter/flutter/issues/130354
|
|
notGCedAllowList: <String, int?>{
|
|
'ValueNotifier<_OverlayEntryWidgetState?>': 2,
|
|
'ValueNotifier<String?>': 1,
|
|
},
|
|
));
|
|
|
|
testWidgetsWithLeakTracking('LicensePage control test with all properties', (WidgetTester tester) async {
|
|
const FlutterLogo logo = FlutterLogo();
|
|
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(<String>['AAA'], 'BBB'),
|
|
]);
|
|
});
|
|
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(
|
|
<String>['Another package'],
|
|
'Another license',
|
|
),
|
|
]);
|
|
});
|
|
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
title: 'Pirate app',
|
|
home: Center(
|
|
child: LicensePage(
|
|
applicationName: 'LicensePage test app',
|
|
applicationVersion: '0.1.2',
|
|
applicationIcon: logo,
|
|
applicationLegalese: 'I am the very model of a modern major general.',
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.text('Pirate app'), findsNothing);
|
|
expect(find.text('LicensePage test app'), findsOneWidget);
|
|
expect(find.text('0.1.2'), findsOneWidget);
|
|
expect(find.byWidget(logo), findsOneWidget);
|
|
expect(
|
|
find.text('I am the very model of a modern major general.'),
|
|
findsOneWidget,
|
|
);
|
|
expect(find.text('AAA'), findsNothing);
|
|
expect(find.text('BBB'), findsNothing);
|
|
expect(find.text('Another package'), findsNothing);
|
|
expect(find.text('Another license'), findsNothing);
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('Pirate app'), findsNothing);
|
|
expect(find.text('LicensePage test app'), findsOneWidget);
|
|
expect(find.text('0.1.2'), findsOneWidget);
|
|
expect(find.byWidget(logo), findsOneWidget);
|
|
expect(
|
|
find.text('I am the very model of a modern major general.'),
|
|
findsOneWidget,
|
|
);
|
|
|
|
// Check for packages.
|
|
expect(find.text('AAA'), findsOneWidget);
|
|
expect(find.text('Another package'), findsOneWidget);
|
|
|
|
// Check license is displayed after entering into license page for 'AAA'.
|
|
await tester.tap(find.text('AAA'));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('BBB'), findsOneWidget);
|
|
|
|
/// Go back to list of packages.
|
|
await tester.pageBack();
|
|
await tester.pumpAndSettle();
|
|
|
|
/// Check license is displayed after entering into license page for
|
|
/// 'Another package'.
|
|
await tester.tap(find.text('Another package'));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('Another license'), findsOneWidget);
|
|
},
|
|
leakTrackingTestConfig: const LeakTrackingTestConfig(
|
|
// TODO(polina-c): remove after fixing
|
|
// https://github.com/flutter/flutter/issues/130354
|
|
notGCedAllowList: <String, int?>{
|
|
'ValueNotifier<_OverlayEntryWidgetState?>':2,
|
|
'ValueNotifier<String?>': 1,
|
|
},
|
|
));
|
|
|
|
testWidgetsWithLeakTracking('Material2 - _PackageLicensePage title style without AppBarTheme', (WidgetTester tester) async {
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(<String>['AAA'], 'BBB'),
|
|
]);
|
|
});
|
|
|
|
const TextStyle titleTextStyle = TextStyle(
|
|
fontSize: 20,
|
|
color: Colors.black,
|
|
inherit: false,
|
|
);
|
|
const TextStyle subtitleTextStyle = TextStyle(
|
|
fontSize: 15,
|
|
color: Colors.red,
|
|
inherit: false,
|
|
);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(
|
|
useMaterial3: false,
|
|
primaryTextTheme: const TextTheme(
|
|
titleLarge: titleTextStyle,
|
|
titleSmall: subtitleTextStyle,
|
|
),
|
|
),
|
|
home: const Center(
|
|
child: LicensePage(),
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
// Check for packages.
|
|
expect(find.text('AAA'), findsOneWidget);
|
|
|
|
// Check license is displayed after entering into license page for 'AAA'.
|
|
await tester.tap(find.text('AAA'));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Check for titles style.
|
|
final Text title = tester.widget(find.text('AAA'));
|
|
expect(title.style, titleTextStyle);
|
|
final Text subtitle = tester.widget(find.text('1 license.'));
|
|
expect(subtitle.style, subtitleTextStyle);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Material3 - _PackageLicensePage title style without AppBarTheme', (WidgetTester tester) async {
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(<String>['AAA'], 'BBB'),
|
|
]);
|
|
});
|
|
|
|
const TextStyle titleTextStyle = TextStyle(
|
|
fontSize: 20,
|
|
color: Colors.black,
|
|
inherit: false,
|
|
);
|
|
const TextStyle subtitleTextStyle = TextStyle(
|
|
fontSize: 15,
|
|
color: Colors.red,
|
|
inherit: false,
|
|
);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(
|
|
useMaterial3: true,
|
|
textTheme: const TextTheme(
|
|
titleLarge: titleTextStyle,
|
|
titleSmall: subtitleTextStyle,
|
|
),
|
|
),
|
|
home: const Center(
|
|
child: LicensePage(),
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
// Check for packages.
|
|
expect(find.text('AAA'), findsOneWidget);
|
|
|
|
// Check license is displayed after entering into license page for 'AAA'.
|
|
await tester.tap(find.text('AAA'));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Check for titles style.
|
|
final Text title = tester.widget(find.text('AAA'));
|
|
expect(title.style, titleTextStyle);
|
|
final Text subtitle = tester.widget(find.text('1 license.'));
|
|
expect(subtitle.style, subtitleTextStyle);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('_PackageLicensePage title style with AppBarTheme', (WidgetTester tester) async {
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(<String>['AAA'], 'BBB'),
|
|
]);
|
|
});
|
|
|
|
const TextStyle titleTextStyle = TextStyle(
|
|
fontSize: 20,
|
|
color: Colors.indigo,
|
|
);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(
|
|
// Not used because appBarTheme is prioritized.
|
|
primaryTextTheme: const TextTheme(
|
|
titleLarge: TextStyle(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
titleSmall: TextStyle(
|
|
fontSize: 10,
|
|
color: Colors.grey,
|
|
),
|
|
),
|
|
appBarTheme: const AppBarTheme(
|
|
titleTextStyle: titleTextStyle,
|
|
foregroundColor: Colors.indigo,
|
|
),
|
|
),
|
|
home: const Center(
|
|
child: LicensePage(),
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
// Check for packages.
|
|
expect(find.text('AAA'), findsOneWidget);
|
|
|
|
// Check license is displayed after entering into license page for 'AAA'.
|
|
await tester.tap(find.text('AAA'));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Check for titles style.
|
|
final Text title = tester.widget(find.text('AAA'));
|
|
expect(title.style, titleTextStyle);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Material2 - LicensePage respects the notch', (WidgetTester tester) async {
|
|
const double safeareaPadding = 27.0;
|
|
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(<String>['ABC'], 'DEF'),
|
|
]);
|
|
});
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: false),
|
|
home: const MediaQuery(
|
|
data: MediaQueryData(
|
|
padding: EdgeInsets.all(safeareaPadding),
|
|
),
|
|
child: LicensePage(),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
// The position of the top left of app bar title should indicate whether
|
|
// the safe area is sufficiently respected.
|
|
expect(
|
|
tester.getTopLeft(find.text('Licenses')),
|
|
const Offset(16.0 + safeareaPadding, 18.0 + safeareaPadding),
|
|
);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Material3 - LicensePage respects the notch', (WidgetTester tester) async {
|
|
const double safeareaPadding = 27.0;
|
|
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(<String>['ABC'], 'DEF'),
|
|
]);
|
|
});
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: true),
|
|
home: const MediaQuery(
|
|
data: MediaQueryData(
|
|
padding: EdgeInsets.all(safeareaPadding),
|
|
),
|
|
child: LicensePage(),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
// The position of the top left of app bar title should indicate whether
|
|
// the safe area is sufficiently respected.
|
|
expect(
|
|
tester.getTopLeft(find.text('Licenses')),
|
|
const Offset(16.0 + safeareaPadding, 14.0 + safeareaPadding),
|
|
);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('LicensePage returns early if unmounted', (WidgetTester tester) async {
|
|
final Completer<LicenseEntry> licenseCompleter = Completer<LicenseEntry>();
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromFuture(licenseCompleter.future);
|
|
});
|
|
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: LicensePage(),
|
|
),
|
|
);
|
|
await tester.pump();
|
|
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: Placeholder(),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle();
|
|
final FakeLicenseEntry licenseEntry = FakeLicenseEntry();
|
|
licenseCompleter.complete(licenseEntry);
|
|
expect(licenseEntry.packagesCalled, false);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('LicensePage returns late if unmounted', (WidgetTester tester) async {
|
|
final Completer<LicenseEntry> licenseCompleter = Completer<LicenseEntry>();
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromFuture(licenseCompleter.future);
|
|
});
|
|
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: LicensePage(),
|
|
),
|
|
);
|
|
await tester.pump();
|
|
final FakeLicenseEntry licenseEntry = FakeLicenseEntry();
|
|
licenseCompleter.complete(licenseEntry);
|
|
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: Placeholder(),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle();
|
|
expect(licenseEntry.packagesCalled, true);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('LicensePage logic defaults to executable name for app name', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
title: 'flutter_tester',
|
|
home: Material(child: LicensePage()),
|
|
),
|
|
);
|
|
expect(find.text('flutter_tester'), findsOneWidget);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('AboutListTile dense property is applied', (WidgetTester tester) async {
|
|
await tester.pumpWidget(const MaterialApp(
|
|
home: Material(child: Center(child: AboutListTile())),
|
|
));
|
|
Rect tileRect = tester.getRect(find.byType(AboutListTile));
|
|
expect(tileRect.height, 56.0);
|
|
|
|
await tester.pumpWidget(const MaterialApp(
|
|
home: Material(child: Center(child: AboutListTile(dense: false))),
|
|
));
|
|
tileRect = tester.getRect(find.byType(AboutListTile));
|
|
expect(tileRect.height, 56.0);
|
|
|
|
await tester.pumpWidget(const MaterialApp(
|
|
home: Material(child: Center(child: AboutListTile(dense: true))),
|
|
));
|
|
tileRect = tester.getRect(find.byType(AboutListTile));
|
|
expect(tileRect.height, 48.0);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('showLicensePage uses nested navigator by default', (WidgetTester tester) async {
|
|
final LicensePageObserver rootObserver = LicensePageObserver();
|
|
final LicensePageObserver nestedObserver = LicensePageObserver();
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
navigatorObservers: <NavigatorObserver>[rootObserver],
|
|
initialRoute: '/',
|
|
onGenerateRoute: (_) {
|
|
return PageRouteBuilder<dynamic>(
|
|
pageBuilder: (_, __, ___) => Navigator(
|
|
observers: <NavigatorObserver>[nestedObserver],
|
|
onGenerateRoute: (RouteSettings settings) {
|
|
return PageRouteBuilder<dynamic>(
|
|
pageBuilder: (BuildContext context, _, __) {
|
|
return ElevatedButton(
|
|
onPressed: () {
|
|
showLicensePage(
|
|
context: context,
|
|
applicationName: 'A',
|
|
);
|
|
},
|
|
child: const Text('Show License Page'),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
);
|
|
},
|
|
));
|
|
|
|
// Open the dialog.
|
|
await tester.tap(find.byType(ElevatedButton));
|
|
|
|
expect(rootObserver.licensePageCount, 0);
|
|
expect(nestedObserver.licensePageCount, 1);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('showLicensePage uses root navigator if useRootNavigator is true', (WidgetTester tester) async {
|
|
final LicensePageObserver rootObserver = LicensePageObserver();
|
|
final LicensePageObserver nestedObserver = LicensePageObserver();
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
navigatorObservers: <NavigatorObserver>[rootObserver],
|
|
initialRoute: '/',
|
|
onGenerateRoute: (_) {
|
|
return PageRouteBuilder<dynamic>(
|
|
pageBuilder: (_, __, ___) => Navigator(
|
|
observers: <NavigatorObserver>[nestedObserver],
|
|
onGenerateRoute: (RouteSettings settings) {
|
|
return PageRouteBuilder<dynamic>(
|
|
pageBuilder: (BuildContext context, _, __) {
|
|
return ElevatedButton(
|
|
onPressed: () {
|
|
showLicensePage(
|
|
context: context,
|
|
useRootNavigator: true,
|
|
applicationName: 'A',
|
|
);
|
|
},
|
|
child: const Text('Show License Page'),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
);
|
|
},
|
|
));
|
|
|
|
// Open the dialog.
|
|
await tester.tap(find.byType(ElevatedButton));
|
|
|
|
expect(rootObserver.licensePageCount, 1);
|
|
expect(nestedObserver.licensePageCount, 0);
|
|
});
|
|
|
|
group('Barrier dismissible', () {
|
|
late AboutDialogObserver rootObserver;
|
|
|
|
setUp(() {
|
|
rootObserver = AboutDialogObserver();
|
|
});
|
|
|
|
testWidgets('Barrier is dismissible with default parameter', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
navigatorObservers: <NavigatorObserver>[rootObserver],
|
|
home: Material(
|
|
child: Center(
|
|
child: Builder(
|
|
builder: (BuildContext context) {
|
|
return ElevatedButton(
|
|
child: const Text('X'),
|
|
onPressed: () => showAboutDialog(
|
|
context: context,
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// Open the dialog.
|
|
await tester.tap(find.byType(ElevatedButton));
|
|
await tester.pumpAndSettle();
|
|
expect(rootObserver.dialogCount, 1);
|
|
|
|
// Tap on the barrier.
|
|
await tester.tapAt(const Offset(10.0, 10.0));
|
|
await tester.pumpAndSettle();
|
|
expect(rootObserver.dialogCount, 0);
|
|
});
|
|
|
|
testWidgets('Barrier is not dismissible with barrierDismissible is false', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
navigatorObservers: <NavigatorObserver>[rootObserver],
|
|
home: Material(
|
|
child: Center(
|
|
child: Builder(
|
|
builder: (BuildContext context) {
|
|
return ElevatedButton(
|
|
child: const Text('X'),
|
|
onPressed: () => showAboutDialog(
|
|
context: context,
|
|
barrierDismissible: false
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// Open the dialog.
|
|
await tester.tap(find.byType(ElevatedButton));
|
|
await tester.pumpAndSettle();
|
|
expect(rootObserver.dialogCount, 1);
|
|
|
|
// Tap on the barrier, which shouldn't do anything this time.
|
|
await tester.tapAt(const Offset(10.0, 10.0));
|
|
await tester.pumpAndSettle();
|
|
expect(rootObserver.dialogCount, 1);
|
|
});
|
|
});
|
|
|
|
testWidgets('Barrier color', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Material(
|
|
child: Center(
|
|
child: Builder(
|
|
builder: (BuildContext context) {
|
|
return ElevatedButton(
|
|
child: const Text('X'),
|
|
onPressed: () => showAboutDialog(
|
|
context: context,
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// Open the dialog.
|
|
await tester.tap(find.byType(ElevatedButton));
|
|
await tester.pumpAndSettle();
|
|
expect(tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).color, Colors.black54);
|
|
|
|
// Dismiss the dialog.
|
|
await tester.tapAt(const Offset(10.0, 10.0));
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Material(
|
|
child: Center(
|
|
child: Builder(
|
|
builder: (BuildContext context) {
|
|
return ElevatedButton(
|
|
child: const Text('X'),
|
|
onPressed: () => showAboutDialog(
|
|
context: context,
|
|
barrierColor: Colors.pink,
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// Open the dialog.
|
|
await tester.tap(find.byType(ElevatedButton));
|
|
await tester.pumpAndSettle();
|
|
expect(tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).color, Colors.pink);
|
|
});
|
|
|
|
testWidgets('Barrier Label', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Material(
|
|
child: Center(
|
|
child: Builder(
|
|
builder: (BuildContext context) {
|
|
return ElevatedButton(
|
|
child: const Text('X'),
|
|
onPressed: () => showAboutDialog(
|
|
context: context,
|
|
barrierLabel: 'Custom Label',
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// Open the dialog.
|
|
await tester.tap(find.byType(ElevatedButton));
|
|
await tester.pumpAndSettle();
|
|
expect(tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).semanticsLabel, 'Custom Label');
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('showAboutDialog uses root navigator by default', (WidgetTester tester) async {
|
|
final AboutDialogObserver rootObserver = AboutDialogObserver();
|
|
final AboutDialogObserver nestedObserver = AboutDialogObserver();
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
navigatorObservers: <NavigatorObserver>[rootObserver],
|
|
home: Navigator(
|
|
observers: <NavigatorObserver>[nestedObserver],
|
|
onGenerateRoute: (RouteSettings settings) {
|
|
return MaterialPageRoute<dynamic>(
|
|
builder: (BuildContext context) {
|
|
return ElevatedButton(
|
|
onPressed: () {
|
|
showAboutDialog(
|
|
context: context,
|
|
applicationName: 'A',
|
|
);
|
|
},
|
|
child: const Text('Show About Dialog'),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
));
|
|
|
|
// Open the dialog.
|
|
await tester.tap(find.byType(ElevatedButton));
|
|
|
|
expect(rootObserver.dialogCount, 1);
|
|
expect(nestedObserver.dialogCount, 0);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('showAboutDialog uses nested navigator if useRootNavigator is false', (WidgetTester tester) async {
|
|
final AboutDialogObserver rootObserver = AboutDialogObserver();
|
|
final AboutDialogObserver nestedObserver = AboutDialogObserver();
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
navigatorObservers: <NavigatorObserver>[rootObserver],
|
|
home: Navigator(
|
|
observers: <NavigatorObserver>[nestedObserver],
|
|
onGenerateRoute: (RouteSettings settings) {
|
|
return MaterialPageRoute<dynamic>(
|
|
builder: (BuildContext context) {
|
|
return ElevatedButton(
|
|
onPressed: () {
|
|
showAboutDialog(
|
|
context: context,
|
|
useRootNavigator: false,
|
|
applicationName: 'A',
|
|
);
|
|
},
|
|
child: const Text('Show About Dialog'),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
));
|
|
|
|
// Open the dialog.
|
|
await tester.tap(find.byType(ElevatedButton));
|
|
|
|
expect(rootObserver.dialogCount, 0);
|
|
expect(nestedObserver.dialogCount, 1);
|
|
});
|
|
|
|
group('showAboutDialog avoids overlapping display features', () {
|
|
testWidgetsWithLeakTracking('default positioning', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
builder: (BuildContext context, Widget? child) {
|
|
return MediaQuery(
|
|
// Display has a vertical hinge down the middle
|
|
data: const MediaQueryData(
|
|
size: Size(800, 600),
|
|
displayFeatures: <DisplayFeature>[
|
|
DisplayFeature(
|
|
bounds: Rect.fromLTRB(390, 0, 410, 600),
|
|
type: DisplayFeatureType.hinge,
|
|
state: DisplayFeatureState.unknown,
|
|
),
|
|
],
|
|
),
|
|
child: child!,
|
|
);
|
|
},
|
|
home: Builder(
|
|
builder: (BuildContext context) => ElevatedButton(
|
|
onPressed: () {
|
|
showAboutDialog(
|
|
context: context,
|
|
useRootNavigator: false,
|
|
applicationName: 'A',
|
|
);
|
|
},
|
|
child: const Text('Show About Dialog'),
|
|
),
|
|
),
|
|
));
|
|
|
|
// Open the dialog.
|
|
await tester.tap(find.byType(ElevatedButton));
|
|
await tester.pumpAndSettle();
|
|
|
|
// By default it should place the dialog on the left screen
|
|
expect(tester.getTopLeft(find.byType(AboutDialog)), Offset.zero);
|
|
expect(tester.getBottomRight(find.byType(AboutDialog)), const Offset(390.0, 600.0));
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('positioning using anchorPoint', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
builder: (BuildContext context, Widget? child) {
|
|
return MediaQuery(
|
|
// Display has a vertical hinge down the middle
|
|
data: const MediaQueryData(
|
|
size: Size(800, 600),
|
|
displayFeatures: <DisplayFeature>[
|
|
DisplayFeature(
|
|
bounds: Rect.fromLTRB(390, 0, 410, 600),
|
|
type: DisplayFeatureType.hinge,
|
|
state: DisplayFeatureState.unknown,
|
|
),
|
|
],
|
|
),
|
|
child: child!,
|
|
);
|
|
},
|
|
home: Builder(
|
|
builder: (BuildContext context) => ElevatedButton(
|
|
onPressed: () {
|
|
showAboutDialog(
|
|
context: context,
|
|
useRootNavigator: false,
|
|
applicationName: 'A',
|
|
anchorPoint: const Offset(1000, 0),
|
|
);
|
|
},
|
|
child: const Text('Show About Dialog'),
|
|
),
|
|
),
|
|
));
|
|
|
|
// Open the dialog.
|
|
await tester.tap(find.byType(ElevatedButton));
|
|
await tester.pumpAndSettle();
|
|
|
|
// The anchorPoint hits the right side of the display
|
|
expect(tester.getTopLeft(find.byType(AboutDialog)), const Offset(410.0, 0.0));
|
|
expect(tester.getBottomRight(find.byType(AboutDialog)), const Offset(800.0, 600.0));
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('positioning using Directionality', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
builder: (BuildContext context, Widget? child) {
|
|
return MediaQuery(
|
|
// Display has a vertical hinge down the middle
|
|
data: const MediaQueryData(
|
|
size: Size(800, 600),
|
|
displayFeatures: <DisplayFeature>[
|
|
DisplayFeature(
|
|
bounds: Rect.fromLTRB(390, 0, 410, 600),
|
|
type: DisplayFeatureType.hinge,
|
|
state: DisplayFeatureState.unknown,
|
|
),
|
|
],
|
|
),
|
|
child: Directionality(
|
|
textDirection: TextDirection.rtl,
|
|
child: child!,
|
|
),
|
|
);
|
|
},
|
|
home: Builder(
|
|
builder: (BuildContext context) => ElevatedButton(
|
|
onPressed: () {
|
|
showAboutDialog(
|
|
context: context,
|
|
useRootNavigator: false,
|
|
applicationName: 'A',
|
|
);
|
|
},
|
|
child: const Text('Show About Dialog'),
|
|
),
|
|
),
|
|
));
|
|
|
|
// Open the dialog.
|
|
await tester.tap(find.byType(ElevatedButton));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Since this is rtl, the first screen is the on the right
|
|
expect(tester.getTopLeft(find.byType(AboutDialog)), const Offset(410.0, 0.0));
|
|
expect(tester.getBottomRight(find.byType(AboutDialog)), const Offset(800.0, 600.0));
|
|
});
|
|
});
|
|
|
|
testWidgetsWithLeakTracking("AboutListTile's child should not be offset when the icon is not specified.", (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: Scaffold(
|
|
body: AboutListTile(
|
|
child: Text('About'),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(
|
|
find.descendant(
|
|
of: find.byType(AboutListTile),
|
|
matching: find.byType(Icon),
|
|
),
|
|
findsNothing,
|
|
);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking("AboutDialog's contents are scrollable", (WidgetTester tester) async {
|
|
final Key contentKey = UniqueKey();
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Navigator(
|
|
onGenerateRoute: (RouteSettings settings) {
|
|
return MaterialPageRoute<dynamic>(
|
|
builder: (BuildContext context) {
|
|
return ElevatedButton(
|
|
onPressed: () {
|
|
showAboutDialog(
|
|
context: context,
|
|
useRootNavigator: false,
|
|
applicationName: 'A',
|
|
children: <Widget>[
|
|
Container(
|
|
key: contentKey,
|
|
color: Colors.orange,
|
|
height: 500,
|
|
),
|
|
],
|
|
);
|
|
},
|
|
child: const Text('Show About Dialog'),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.text('Show About Dialog'));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Try dragging by the [AboutDialog]'s title.
|
|
RenderBox box = tester.renderObject(find.text('A'));
|
|
Offset originalOffset = box.localToGlobal(Offset.zero);
|
|
await tester.drag(find.byKey(contentKey), const Offset(0.0, -20.0));
|
|
|
|
expect(box.localToGlobal(Offset.zero), equals(originalOffset.translate(0.0, -20.0)));
|
|
|
|
// Try dragging by the additional children in contents.
|
|
box = tester.renderObject(find.byKey(contentKey));
|
|
originalOffset = box.localToGlobal(Offset.zero);
|
|
await tester.drag(find.byKey(contentKey), const Offset(0.0, -20.0));
|
|
|
|
expect(box.localToGlobal(Offset.zero), equals(originalOffset.translate(0.0, -20.0)));
|
|
});
|
|
|
|
testWidgetsWithLeakTracking("Material2 - LicensePage's color must be same whether loading or done", (WidgetTester tester) async {
|
|
const Color scaffoldColor = Color(0xFF123456);
|
|
const Color cardColor = Color(0xFF654321);
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: ThemeData.light(useMaterial3: false).copyWith(
|
|
scaffoldBackgroundColor: scaffoldColor,
|
|
cardColor: cardColor,
|
|
),
|
|
home: Scaffold(
|
|
body: Center(
|
|
child: Builder(
|
|
builder: (BuildContext context) => GestureDetector(
|
|
child: const Text('Show licenses'),
|
|
onTap: () {
|
|
showLicensePage(
|
|
context: context,
|
|
applicationName: 'MyApp',
|
|
applicationVersion: '1.0.0',
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.text('Show licenses'));
|
|
await tester.pump();
|
|
await tester.pump();
|
|
|
|
// Check color when loading.
|
|
final List<Material> materialLoadings = tester.widgetList<Material>(find.byType(Material)).toList();
|
|
expect(materialLoadings.length, equals(4));
|
|
expect(materialLoadings[1].color, scaffoldColor);
|
|
expect(materialLoadings[2].color, cardColor);
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
// Check color when done.
|
|
expect(find.byKey(const ValueKey<ConnectionState>(ConnectionState.done)), findsOneWidget);
|
|
final List<Material> materialDones = tester.widgetList<Material>(find.byType(Material)).toList();
|
|
expect(materialDones.length, equals(3));
|
|
expect(materialDones[0].color, scaffoldColor);
|
|
expect(materialDones[1].color, cardColor);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking("Material3 - LicensePage's color must be same whether loading or done", (WidgetTester tester) async {
|
|
const Color scaffoldColor = Color(0xFF123456);
|
|
const Color cardColor = Color(0xFF654321);
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
theme: ThemeData.light(useMaterial3: true).copyWith(
|
|
scaffoldBackgroundColor: scaffoldColor,
|
|
cardColor: cardColor,
|
|
),
|
|
home: Scaffold(
|
|
body: Center(
|
|
child: Builder(
|
|
builder: (BuildContext context) => GestureDetector(
|
|
child: const Text('Show licenses'),
|
|
onTap: () {
|
|
showLicensePage(
|
|
context: context,
|
|
applicationName: 'MyApp',
|
|
applicationVersion: '1.0.0',
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.text('Show licenses'));
|
|
await tester.pump();
|
|
await tester.pump();
|
|
|
|
// Check color when loading.
|
|
final List<Material> materialLoadings = tester.widgetList<Material>(find.byType(Material)).toList();
|
|
expect(materialLoadings.length, equals(5));
|
|
expect(materialLoadings[1].color, scaffoldColor);
|
|
expect(materialLoadings[2].color, cardColor);
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
// Check color when done.
|
|
expect(find.byKey(const ValueKey<ConnectionState>(ConnectionState.done)), findsOneWidget);
|
|
final List<Material> materialDones = tester.widgetList<Material>(find.byType(Material)).toList();
|
|
expect(materialDones.length, equals(4));
|
|
expect(materialDones[0].color, scaffoldColor);
|
|
expect(materialDones[1].color, cardColor);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Conflicting scrollbars are not applied by ScrollBehavior to _PackageLicensePage', (WidgetTester tester) async {
|
|
// Regression test for https://github.com/flutter/flutter/issues/83819
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(<String>['AAA'], 'BBB'),
|
|
]);
|
|
});
|
|
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: Center(
|
|
child: LicensePage(),
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
// Check for packages.
|
|
expect(find.text('AAA'), findsOneWidget);
|
|
// Check license is displayed after entering into license page for 'AAA'.
|
|
await tester.tap(find.text('AAA'));
|
|
await tester.pumpAndSettle();
|
|
|
|
// The inherited ScrollBehavior should not apply Scrollbars since they are
|
|
// already built in to the widget.
|
|
switch (debugDefaultTargetPlatformOverride) {
|
|
case TargetPlatform.android:
|
|
case TargetPlatform.fuchsia:
|
|
case TargetPlatform.linux:
|
|
case TargetPlatform.macOS:
|
|
case TargetPlatform.windows:
|
|
expect(find.byType(CupertinoScrollbar), findsNothing);
|
|
case TargetPlatform.iOS:
|
|
expect(find.byType(CupertinoScrollbar), findsOneWidget);
|
|
case null:
|
|
break;
|
|
}
|
|
expect(find.byType(Scrollbar), findsOneWidget);
|
|
expect(find.byType(RawScrollbar), findsNothing);
|
|
|
|
}, variant: TargetPlatformVariant.all());
|
|
|
|
testWidgetsWithLeakTracking('ListView of license entries is primary', (WidgetTester tester) async {
|
|
// Regression test for https://github.com/flutter/flutter/issues/120710
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
LicenseEntryWithLineBreaks(
|
|
<String>['AAA'],
|
|
// Add enough content to scroll
|
|
List<String>.generate(500, (int index) => 'BBBB').join('\n'),
|
|
),
|
|
]);
|
|
});
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
title: 'Flutter Code Sample',
|
|
home: Scaffold(
|
|
body: Builder(
|
|
builder: (BuildContext context) => TextButton(
|
|
child: const Text('Show License Page'),
|
|
onPressed: () {
|
|
showLicensePage(context: context);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
)
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('Show License Page'), findsOneWidget);
|
|
await tester.tap(find.text('Show License Page'));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Check for packages.
|
|
expect(find.text('AAA'), findsOneWidget);
|
|
// Check license is displayed after entering into license page for 'AAA'.
|
|
await tester.tap(find.text('AAA'));
|
|
await tester.pumpAndSettle();
|
|
|
|
// The inherited ScrollBehavior should not apply Scrollbars since they are
|
|
// already built in to the widget.
|
|
switch (debugDefaultTargetPlatformOverride) {
|
|
case TargetPlatform.android:
|
|
case TargetPlatform.fuchsia:
|
|
case TargetPlatform.linux:
|
|
case TargetPlatform.macOS:
|
|
case TargetPlatform.windows:
|
|
expect(find.byType(CupertinoScrollbar), findsNothing);
|
|
case TargetPlatform.iOS:
|
|
expect(find.byType(CupertinoScrollbar), findsOneWidget);
|
|
case null:
|
|
break;
|
|
}
|
|
expect(find.byType(Scrollbar), findsOneWidget);
|
|
expect(find.byType(RawScrollbar), findsNothing);
|
|
await tester.drag(find.byType(ListView), const Offset(0.0, 20.0));
|
|
await tester.pumpAndSettle(); // No exception triggered.
|
|
}, variant: TargetPlatformVariant.all());
|
|
|
|
testWidgetsWithLeakTracking('LicensePage padding', (WidgetTester tester) async {
|
|
const FlutterLogo logo = FlutterLogo();
|
|
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
title: 'Pirate app',
|
|
home: Center(
|
|
child: LicensePage(
|
|
applicationName: 'LicensePage test app',
|
|
applicationIcon: logo,
|
|
applicationVersion: '0.1.2',
|
|
applicationLegalese: 'I am the very model of a modern major general.',
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder appName = find.text('LicensePage test app');
|
|
final Finder appIcon = find.byType(FlutterLogo);
|
|
final Finder appVersion = find.text('0.1.2');
|
|
final Finder appLegalese = find.text('I am the very model of a modern major general.');
|
|
final Finder appPowered = find.text('Powered by Flutter');
|
|
|
|
expect(appName, findsOneWidget);
|
|
expect(appIcon, findsOneWidget);
|
|
expect(appVersion, findsOneWidget);
|
|
expect(appLegalese, findsOneWidget);
|
|
expect(appPowered, findsOneWidget);
|
|
|
|
// Bottom padding is applied to the app version and app legalese text.
|
|
final double appNameBottomPadding = tester.getTopLeft(appIcon).dy - tester.getBottomLeft(appName).dy;
|
|
expect(appNameBottomPadding, 0.0);
|
|
|
|
final double appIconBottomPadding = tester.getTopLeft(appVersion).dy - tester.getBottomLeft(appIcon).dy;
|
|
expect(appIconBottomPadding, 0.0);
|
|
|
|
final double appVersionBottomPadding = tester.getTopLeft(appLegalese).dy - tester.getBottomLeft(appVersion).dy;
|
|
expect(appVersionBottomPadding, 18.0);
|
|
|
|
final double appLegaleseBottomPadding = tester.getTopLeft(appPowered).dy - tester.getBottomLeft(appLegalese).dy;
|
|
expect(appLegaleseBottomPadding, 18.0);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('LicensePage has no extra padding between app icon and app powered text', (WidgetTester tester) async {
|
|
// This is a regression test for https://github.com/flutter/flutter/issues/99559
|
|
|
|
const FlutterLogo logo = FlutterLogo();
|
|
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
title: 'Pirate app',
|
|
home: Center(
|
|
child: LicensePage(
|
|
applicationIcon: logo,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder appName = find.text('LicensePage test app');
|
|
final Finder appIcon = find.byType(FlutterLogo);
|
|
final Finder appVersion = find.text('0.1.2');
|
|
final Finder appLegalese = find.text('I am the very model of a modern major general.');
|
|
final Finder appPowered = find.text('Powered by Flutter');
|
|
|
|
expect(appName, findsNothing);
|
|
expect(appIcon, findsOneWidget);
|
|
expect(appVersion, findsNothing);
|
|
expect(appLegalese, findsNothing);
|
|
expect(appPowered, findsOneWidget);
|
|
|
|
// Padding between app icon and app powered text.
|
|
final double appIconBottomPadding = tester.getTopLeft(appPowered).dy - tester.getBottomLeft(appIcon).dy;
|
|
expect(appIconBottomPadding, 18.0);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Material2 - Error handling test', (WidgetTester tester) async {
|
|
LicenseRegistry.addLicense(() => Stream<LicenseEntry>.error(Exception('Injected failure')));
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: false),
|
|
home: const Material(child: AboutListTile())
|
|
)
|
|
);
|
|
await tester.tap(find.byType(ListTile));
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await tester.tap(find.text('VIEW LICENSES'));
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 2));
|
|
final Finder finder = find.byWidgetPredicate((Widget widget) => widget.runtimeType.toString() == '_PackagesView');
|
|
// force the stream to complete (has to be done in a runAsync block since it's areal async process)
|
|
await tester.runAsync(() => (tester.firstState(finder) as dynamic).licenses as Future<dynamic>); // ignore: avoid_dynamic_calls
|
|
expect(tester.takeException().toString(), 'Exception: Injected failure');
|
|
await tester.pumpAndSettle();
|
|
expect(tester.takeException().toString(), 'Exception: Injected failure');
|
|
expect(find.text('Exception: Injected failure'), findsOneWidget);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Material3 - Error handling test', (WidgetTester tester) async {
|
|
LicenseRegistry.addLicense(() => Stream<LicenseEntry>.error(Exception('Injected failure')));
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: true),
|
|
home: const Material(child: AboutListTile())
|
|
)
|
|
);
|
|
await tester.tap(find.byType(ListTile));
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await tester.tap(find.text('View licenses'));
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 2));
|
|
final Finder finder = find.byWidgetPredicate((Widget widget) => widget.runtimeType.toString() == '_PackagesView');
|
|
// force the stream to complete (has to be done in a runAsync block since it's areal async process)
|
|
await tester.runAsync(() => (tester.firstState(finder) as dynamic).licenses as Future<dynamic>); // ignore: avoid_dynamic_calls
|
|
expect(tester.takeException().toString(), 'Exception: Injected failure');
|
|
await tester.pumpAndSettle();
|
|
expect(tester.takeException().toString(), 'Exception: Injected failure');
|
|
expect(find.text('Exception: Injected failure'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('Material2 - LicensePage master view layout position - ltr', (WidgetTester tester) async {
|
|
const TextDirection textDirection = TextDirection.ltr;
|
|
const Size defaultSize = Size(800.0, 600.0);
|
|
const Size wideSize = Size(1200.0, 600.0);
|
|
const String title = 'License ABC';
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(<String>['ABC'], 'DEF'),
|
|
]);
|
|
});
|
|
|
|
addTearDown(() async {
|
|
await tester.binding.setSurfaceSize(null);
|
|
});
|
|
|
|
// Configure to show the default layout.
|
|
await tester.binding.setSurfaceSize(defaultSize);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: false),
|
|
title: title,
|
|
home: const Scaffold(
|
|
body: Directionality(
|
|
textDirection: textDirection,
|
|
child: LicensePage(),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle(); // Finish rendering the page.
|
|
|
|
// If the layout width is less than 840.0 pixels, nested layout is
|
|
// used which positions license page title at the top center.
|
|
Offset titleOffset = tester.getCenter(find.text(title));
|
|
expect(titleOffset, Offset(defaultSize.width / 2, 92.0));
|
|
expect(tester.getCenter(find.byType(ListView)), Offset(defaultSize.width / 2, 328.0));
|
|
|
|
// Configure a wide window to show the lateral UI.
|
|
await tester.binding.setSurfaceSize(wideSize);
|
|
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
title: title,
|
|
home: Scaffold(
|
|
body: Directionality(
|
|
textDirection: textDirection,
|
|
child: LicensePage(),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle(); // Finish rendering the page.
|
|
|
|
// If the layout width is greater than 840.0 pixels, lateral UI layout
|
|
// is used which positions license page title and packageList
|
|
// at the top left.
|
|
titleOffset = tester.getTopRight(find.text(title));
|
|
expect(titleOffset, const Offset(292.0, 136.0));
|
|
expect(titleOffset.dx, lessThan(wideSize.width - 320)); // Default master view width is 320.0.
|
|
expect(tester.getCenter(find.byType(ListView)), const Offset(160, 356));
|
|
});
|
|
|
|
testWidgets('Material3 - LicensePage master view layout position - ltr', (WidgetTester tester) async {
|
|
const TextDirection textDirection = TextDirection.ltr;
|
|
const Size defaultSize = Size(800.0, 600.0);
|
|
const Size wideSize = Size(1200.0, 600.0);
|
|
const String title = 'License ABC';
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(<String>['ABC'], 'DEF'),
|
|
]);
|
|
});
|
|
|
|
addTearDown(() async {
|
|
await tester.binding.setSurfaceSize(null);
|
|
});
|
|
|
|
// Configure to show the default layout.
|
|
await tester.binding.setSurfaceSize(defaultSize);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: true),
|
|
title: title,
|
|
home: const Scaffold(
|
|
body: Directionality(
|
|
textDirection: textDirection,
|
|
child: LicensePage(),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle(); // Finish rendering the page.
|
|
|
|
// If the layout width is less than 840.0 pixels, nested layout is
|
|
// used which positions license page title at the top center.
|
|
Offset titleOffset = tester.getCenter(find.text(title));
|
|
expect(titleOffset, Offset(defaultSize.width / 2, 96.0));
|
|
expect(tester.getCenter(find.byType(ListView)), Offset(defaultSize.width / 2, 328.0));
|
|
|
|
// Configure a wide window to show the lateral UI.
|
|
await tester.binding.setSurfaceSize(wideSize);
|
|
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
title: title,
|
|
home: Scaffold(
|
|
body: Directionality(
|
|
textDirection: textDirection,
|
|
child: LicensePage(),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle(); // Finish rendering the page.
|
|
|
|
// If the layout width is greater than 840.0 pixels, lateral UI layout
|
|
// is used which positions license page title and packageList
|
|
// at the top left.
|
|
titleOffset = tester.getTopRight(find.text(title));
|
|
expect(titleOffset, const Offset(292.0, 136.0));
|
|
expect(titleOffset.dx, lessThan(wideSize.width - 320)); // Default master view width is 320.0.
|
|
expect(tester.getCenter(find.byType(ListView)), const Offset(160, 356));
|
|
});
|
|
|
|
testWidgets('Material2 - LicensePage master view layout position - rtl', (WidgetTester tester) async {
|
|
const TextDirection textDirection = TextDirection.rtl;
|
|
const Size defaultSize = Size(800.0, 600.0);
|
|
const Size wideSize = Size(1200.0, 600.0);
|
|
const String title = 'License ABC';
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(<String>['ABC'], 'DEF'),
|
|
]);
|
|
});
|
|
|
|
addTearDown(() async {
|
|
await tester.binding.setSurfaceSize(null);
|
|
});
|
|
|
|
// Configure to show the default layout.
|
|
await tester.binding.setSurfaceSize(defaultSize);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: false),
|
|
title: title,
|
|
home: const Scaffold(
|
|
body: Directionality(
|
|
textDirection: textDirection,
|
|
child: LicensePage(),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle(); // Finish rendering the page.
|
|
|
|
// If the layout width is less than 840.0 pixels, nested layout is
|
|
// used which positions license page title at the top center.
|
|
Offset titleOffset = tester.getCenter(find.text(title));
|
|
expect(titleOffset, Offset(defaultSize.width / 2, 92.0));
|
|
expect(tester.getCenter(find.byType(ListView)), Offset(defaultSize.width / 2, 328.0));
|
|
|
|
// Configure a wide window to show the lateral UI.
|
|
await tester.binding.setSurfaceSize(wideSize);
|
|
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
title: title,
|
|
home: Scaffold(
|
|
body: Directionality(
|
|
textDirection: textDirection,
|
|
child: LicensePage(),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle(); // Finish rendering the page.
|
|
|
|
// If the layout width is greater than 840.0 pixels, lateral UI layout
|
|
// is used which positions license page title and packageList
|
|
// at the top right.
|
|
titleOffset = tester.getTopLeft(find.text(title));
|
|
expect(titleOffset, const Offset(908.0, 136.0));
|
|
expect(titleOffset.dx, greaterThan(wideSize.width - 320)); // Default master view width is 320.0.
|
|
expect(tester.getCenter(find.byType(ListView)), const Offset(1040.0, 356.0));
|
|
});
|
|
|
|
testWidgets('Material3 - LicensePage master view layout position - rtl', (WidgetTester tester) async {
|
|
const TextDirection textDirection = TextDirection.rtl;
|
|
const Size defaultSize = Size(800.0, 600.0);
|
|
const Size wideSize = Size(1200.0, 600.0);
|
|
const String title = 'License ABC';
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(<String>['ABC'], 'DEF'),
|
|
]);
|
|
});
|
|
|
|
addTearDown(() async {
|
|
await tester.binding.setSurfaceSize(null);
|
|
});
|
|
|
|
// Configure to show the default layout.
|
|
await tester.binding.setSurfaceSize(defaultSize);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: true),
|
|
title: title,
|
|
home: const Scaffold(
|
|
body: Directionality(
|
|
textDirection: textDirection,
|
|
child: LicensePage(),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle(); // Finish rendering the page.
|
|
|
|
// If the layout width is less than 840.0 pixels, nested layout is
|
|
// used which positions license page title at the top center.
|
|
Offset titleOffset = tester.getCenter(find.text(title));
|
|
expect(titleOffset, Offset(defaultSize.width / 2, 96.0));
|
|
expect(tester.getCenter(find.byType(ListView)), Offset(defaultSize.width / 2, 328.0));
|
|
|
|
// Configure a wide window to show the lateral UI.
|
|
await tester.binding.setSurfaceSize(wideSize);
|
|
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
title: title,
|
|
home: Scaffold(
|
|
body: Directionality(
|
|
textDirection: textDirection,
|
|
child: LicensePage(),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle(); // Finish rendering the page.
|
|
|
|
// If the layout width is greater than 840.0 pixels, lateral UI layout
|
|
// is used which positions license page title and packageList
|
|
// at the top right.
|
|
titleOffset = tester.getTopLeft(find.text(title));
|
|
expect(titleOffset, const Offset(908.0, 136.0));
|
|
expect(titleOffset.dx, greaterThan(wideSize.width - 320)); // Default master view width is 320.0.
|
|
expect(tester.getCenter(find.byType(ListView)), const Offset(1040.0, 356.0));
|
|
});
|
|
|
|
testWidgets('License page title in lateral UI does not use AppBarTheme.foregroundColor', (WidgetTester tester) async {
|
|
// This is a regression test for https://github.com/flutter/flutter/issues/108991
|
|
final ThemeData theme = ThemeData(
|
|
appBarTheme: const AppBarTheme(foregroundColor: Color(0xFFFFFFFF)),
|
|
useMaterial3: true,
|
|
);
|
|
const String title = 'License ABC';
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(<String>['ABC'], 'DEF'),
|
|
]);
|
|
});
|
|
|
|
addTearDown(() async {
|
|
await tester.binding.setSurfaceSize(null);
|
|
});
|
|
|
|
// Configure a wide window to show the lateral UI.
|
|
await tester.binding.setSurfaceSize(const Size(1200.0, 600.0));
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
title: title,
|
|
theme: theme,
|
|
home: const Scaffold(
|
|
body: LicensePage(),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle(); // Finish rendering the page.
|
|
|
|
final RenderParagraph renderParagraph = tester.renderObject(find.text('ABC').last) as RenderParagraph;
|
|
|
|
// License page title should not use AppBarTheme's foregroundColor.
|
|
expect(renderParagraph.text.style!.color, isNot(theme.appBarTheme.foregroundColor));
|
|
|
|
// License page title in the lateral UI uses default text style color.
|
|
expect(renderParagraph.text.style!.color, theme.textTheme.titleLarge!.color);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('License page default title text color in the nested UI', (WidgetTester tester) async {
|
|
// This is a regression test for https://github.com/flutter/flutter/issues/108991
|
|
final ThemeData theme = ThemeData(useMaterial3: true);
|
|
const String title = 'License ABC';
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(<String>['ABC'], 'DEF'),
|
|
]);
|
|
});
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
title: title,
|
|
theme: theme,
|
|
home: const Scaffold(
|
|
body: LicensePage(),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle(); // Finish rendering the page.
|
|
|
|
// Currently in the master view.
|
|
expect(find.text('License ABC'), findsOneWidget);
|
|
|
|
// Navigate to the license page.
|
|
await tester.tap(find.text('ABC'));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Master view is no longer visible.
|
|
expect(find.text('License ABC'), findsNothing);
|
|
|
|
final RenderParagraph renderParagraph = tester.renderObject(find.text('ABC').first) as RenderParagraph;
|
|
expect(renderParagraph.text.style!.color, theme.textTheme.titleLarge!.color);
|
|
});
|
|
|
|
group('Material 2', () {
|
|
// These tests are only relevant for Material 2. Once Material 2
|
|
// support is deprecated and the APIs are removed, these tests
|
|
// can be deleted.
|
|
|
|
testWidgetsWithLeakTracking('License page default title text color in the nested UI', (WidgetTester tester) async {
|
|
// This is a regression test for https://github.com/flutter/flutter/issues/108991
|
|
final ThemeData theme = ThemeData(useMaterial3: false);
|
|
const String title = 'License ABC';
|
|
LicenseRegistry.addLicense(() {
|
|
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
|
|
const LicenseEntryWithLineBreaks(<String>['ABC'], 'DEF'),
|
|
]);
|
|
});
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
title: title,
|
|
theme: theme,
|
|
home: const Scaffold(
|
|
body: LicensePage(),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle(); // Finish rendering the page.
|
|
|
|
// Currently in the master view.
|
|
expect(find.text('License ABC'), findsOneWidget);
|
|
|
|
// Navigate to the license page.
|
|
await tester.tap(find.text('ABC'));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Master view is no longer visible.
|
|
expect(find.text('License ABC'), findsNothing);
|
|
|
|
final RenderParagraph renderParagraph = tester.renderObject(find.text('ABC').first) as RenderParagraph;
|
|
expect(renderParagraph.text.style!.color, theme.primaryTextTheme.titleLarge!.color);
|
|
});
|
|
});
|
|
}
|
|
|
|
class FakeLicenseEntry extends LicenseEntry {
|
|
FakeLicenseEntry();
|
|
|
|
bool get packagesCalled => _packagesCalled;
|
|
bool _packagesCalled = false;
|
|
|
|
@override
|
|
Iterable<LicenseParagraph> paragraphs = <LicenseParagraph>[];
|
|
|
|
@override
|
|
Iterable<String> get packages {
|
|
_packagesCalled = true;
|
|
return <String>[];
|
|
}
|
|
}
|
|
|
|
class LicensePageObserver extends NavigatorObserver {
|
|
int licensePageCount = 0;
|
|
|
|
@override
|
|
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
|
if (route is MaterialPageRoute<dynamic>) {
|
|
licensePageCount++;
|
|
}
|
|
super.didPush(route, previousRoute);
|
|
}
|
|
}
|
|
|
|
class AboutDialogObserver extends NavigatorObserver {
|
|
int dialogCount = 0;
|
|
|
|
@override
|
|
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
|
if (route is DialogRoute) {
|
|
dialogCount++;
|
|
}
|
|
super.didPush(route, previousRoute);
|
|
}
|
|
|
|
@override
|
|
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
|
if (route is DialogRoute) {
|
|
dialogCount--;
|
|
}
|
|
super.didPop(route, previousRoute);
|
|
}
|
|
}
|