400 lines
12 KiB
Dart
400 lines
12 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.
|
|
|
|
// reduced-test-set:
|
|
// This file is run as part of a reduced test set in CI on Mac and Windows
|
|
// machines.
|
|
@Tags(<String>['reduced-test-set'])
|
|
library;
|
|
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/semantics.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
void main() {
|
|
const expansionDuration = Duration(milliseconds: 250);
|
|
const infinitesimalDuration = Duration(microseconds: 1);
|
|
testWidgets('Toggles expansion on tap', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const CupertinoApp(
|
|
home: CupertinoPageScaffold(
|
|
child: CupertinoExpansionTile(title: Text('Title'), child: Text('Content')),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.text('Content'), findsNothing);
|
|
|
|
await tester.tap(find.text('Title'));
|
|
await tester.pump();
|
|
// The child animating its height and a clone fading in.
|
|
expect(find.text('Content'), findsNWidgets(2));
|
|
|
|
await tester.tap(find.text('Title'));
|
|
await tester.pump();
|
|
expect(find.text('Content'), findsNothing);
|
|
});
|
|
|
|
testWidgets('Can be controlled by ExpansibleController', (WidgetTester tester) async {
|
|
final controller = ExpansibleController();
|
|
await tester.pumpWidget(
|
|
CupertinoApp(
|
|
home: CupertinoPageScaffold(
|
|
child: CupertinoExpansionTile(
|
|
controller: controller,
|
|
title: const Text('Title'),
|
|
child: const Text('Content'),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(controller.isExpanded, isFalse);
|
|
expect(find.text('Content'), findsNothing);
|
|
|
|
controller.expand();
|
|
await tester.pump();
|
|
expect(controller.isExpanded, isTrue);
|
|
expect(find.text('Content'), findsOneWidget);
|
|
|
|
controller.collapse();
|
|
await tester.pump();
|
|
expect(controller.isExpanded, isFalse);
|
|
expect(find.text('Content'), findsNothing);
|
|
|
|
controller.dispose();
|
|
});
|
|
|
|
testWidgets('Controller can set the tile to be initially expanded', (WidgetTester tester) async {
|
|
final controller = ExpansibleController();
|
|
await tester.pumpWidget(
|
|
CupertinoApp(
|
|
home: CupertinoPageScaffold(
|
|
child: CupertinoExpansionTile(
|
|
controller: controller,
|
|
title: const Text('Title'),
|
|
child: const Text('Content'),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
controller.expand();
|
|
await tester.pump();
|
|
|
|
expect(controller.isExpanded, isTrue);
|
|
expect(find.text('Content'), findsOneWidget);
|
|
|
|
await tester.tap(find.text('Title'));
|
|
await tester.pump();
|
|
expect(controller.isExpanded, isFalse);
|
|
expect(find.text('Content'), findsNothing);
|
|
|
|
controller.dispose();
|
|
});
|
|
|
|
testWidgets('Nested expansion tile', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const CupertinoApp(
|
|
home: CupertinoPageScaffold(
|
|
child: CupertinoExpansionTile(
|
|
title: Text('Outer'),
|
|
child: CupertinoExpansionTile(title: Text('Inner'), child: Text('Content')),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.text('Content'), findsNothing);
|
|
|
|
await tester.tap(find.text('Outer'));
|
|
await tester.pump();
|
|
await tester.pump(expansionDuration + infinitesimalDuration);
|
|
expect(find.text('Content'), findsNothing);
|
|
|
|
await tester.tap(find.text('Inner'));
|
|
await tester.pump();
|
|
await tester.pump(expansionDuration + infinitesimalDuration);
|
|
expect(find.text('Content'), findsOneWidget);
|
|
|
|
await tester.tap(find.text('Inner'));
|
|
await tester.pump();
|
|
await tester.pump(expansionDuration + infinitesimalDuration);
|
|
expect(find.text('Content'), findsNothing);
|
|
|
|
await tester.tap(find.text('Outer'));
|
|
await tester.pump();
|
|
await tester.pump(expansionDuration + infinitesimalDuration);
|
|
expect(find.text('Content'), findsNothing);
|
|
});
|
|
|
|
testWidgets('Default expansion animation and icon rotation', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const CupertinoApp(
|
|
home: Center(
|
|
child: RepaintBoundary(
|
|
child: CupertinoExpansionTile(
|
|
title: Text('Title'),
|
|
child: SizedBox(height: 50.0, child: ColoredBox(color: Color(0xffff0000))),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await expectLater(
|
|
find.byType(CupertinoExpansionTile),
|
|
matchesGoldenFile('expansion_tile.default.collapsed.png'),
|
|
);
|
|
|
|
await tester.tap(find.text('Title'));
|
|
await tester.pump();
|
|
|
|
// Pump until halfway through the animation.
|
|
await tester.pump(expansionDuration ~/ 2);
|
|
await expectLater(
|
|
find.byType(CupertinoExpansionTile),
|
|
matchesGoldenFile('expansion_tile.default.forward.png'),
|
|
);
|
|
|
|
await tester.pumpAndSettle();
|
|
await expectLater(
|
|
find.byType(CupertinoExpansionTile),
|
|
matchesGoldenFile('expansion_tile.default.expanded.png'),
|
|
);
|
|
|
|
await tester.tap(find.text('Title'));
|
|
await tester.pump();
|
|
|
|
// Pump until halfway through the animation.
|
|
await tester.pump(expansionDuration ~/ 2);
|
|
await expectLater(
|
|
find.byType(CupertinoExpansionTile),
|
|
matchesGoldenFile('expansion_tile.default.reverse.png'),
|
|
);
|
|
});
|
|
|
|
testWidgets('Expansion animation in scroll mode', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const CupertinoApp(
|
|
home: Center(
|
|
child: RepaintBoundary(
|
|
child: CupertinoExpansionTile(
|
|
title: Text('Title'),
|
|
transitionMode: ExpansionTileTransitionMode.scroll,
|
|
child: SizedBox(height: 50.0, child: ColoredBox(color: Color(0xffff0000))),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.tap(find.text('Title'));
|
|
await tester.pump();
|
|
|
|
// Pump until halfway through the animation.
|
|
await tester.pump(expansionDuration ~/ 2);
|
|
await expectLater(
|
|
find.byType(CupertinoExpansionTile),
|
|
matchesGoldenFile('expansion_tile.scroll_mode.forward.png'),
|
|
);
|
|
|
|
await tester.pumpAndSettle();
|
|
await tester.tap(find.text('Title'));
|
|
await tester.pump();
|
|
|
|
// Pump until halfway through the animation.
|
|
await tester.pump(expansionDuration ~/ 2);
|
|
await expectLater(
|
|
find.byType(CupertinoExpansionTile),
|
|
matchesGoldenFile('expansion_tile.scroll_mode.reverse.png'),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
});
|
|
|
|
testWidgets('Nested CupertinoListTile Semantics', (WidgetTester tester) async {
|
|
final controller = ExpansibleController();
|
|
addTearDown(controller.dispose);
|
|
final SemanticsHandle handle = tester.ensureSemantics();
|
|
|
|
await tester.pumpWidget(
|
|
CupertinoApp(
|
|
home: Column(
|
|
children: <Widget>[
|
|
const CupertinoExpansionTile(
|
|
title: Text('First Expansion Tile'),
|
|
child: SizedBox(height: 50.0),
|
|
),
|
|
CupertinoExpansionTile(
|
|
controller: controller,
|
|
title: const Text('Second Expansion Tile'),
|
|
child: const SizedBox(height: 50.0),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
controller.expand();
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
tester.getSemantics(find.byType(CupertinoListTile).first),
|
|
matchesSemantics(
|
|
hasTapAction: true,
|
|
label: 'First Expansion Tile',
|
|
textDirection: TextDirection.ltr,
|
|
),
|
|
);
|
|
|
|
expect(
|
|
tester.getSemantics(find.byType(CupertinoListTile).last),
|
|
matchesSemantics(
|
|
hasTapAction: true,
|
|
label: 'Second Expansion Tile',
|
|
textDirection: TextDirection.ltr,
|
|
),
|
|
);
|
|
handle.dispose();
|
|
});
|
|
|
|
testWidgets('Semantics with the onTapHint is an ancestor of CupertinoListTile', (
|
|
WidgetTester tester,
|
|
) async {
|
|
final controller = ExpansibleController();
|
|
addTearDown(controller.dispose);
|
|
final SemanticsHandle handle = tester.ensureSemantics();
|
|
const localizations = DefaultCupertinoLocalizations();
|
|
|
|
await tester.pumpWidget(
|
|
CupertinoApp(
|
|
home: Column(
|
|
children: <Widget>[
|
|
const CupertinoExpansionTile(
|
|
title: Text('First Expansion Tile'),
|
|
child: SizedBox(height: 100, width: 100),
|
|
),
|
|
CupertinoExpansionTile(
|
|
controller: controller,
|
|
title: const Text('Second Expansion Tile'),
|
|
child: const SizedBox(height: 100, width: 100),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
controller.expand();
|
|
await tester.pumpAndSettle();
|
|
|
|
SemanticsNode semantics = tester.getSemantics(
|
|
find
|
|
.ancestor(of: find.byType(CupertinoListTile).first, matching: find.byType(Semantics))
|
|
.first,
|
|
);
|
|
expect(semantics, isNotNull);
|
|
// The onTapHint is passed to semantics properties's hintOverrides.
|
|
expect(semantics.hintOverrides, isNotNull);
|
|
// The hint should be the opposite of the current state.
|
|
// The first CupertinoExpansionTile is collapsed, so the hint should be
|
|
// "double tap to expand".
|
|
expect(semantics.hintOverrides!.onTapHint, localizations.expansionTileCollapsedTapHint);
|
|
|
|
semantics = tester.getSemantics(
|
|
find
|
|
.ancestor(of: find.byType(CupertinoListTile).last, matching: find.byType(Semantics))
|
|
.first,
|
|
);
|
|
|
|
expect(semantics, isNotNull);
|
|
// The onTapHint is passed to semantics properties's hintOverrides.
|
|
expect(semantics.hintOverrides, isNotNull);
|
|
// The hint should be the opposite of the current state.
|
|
// The second CupertinoExpansionTile is expanded, so the hint should be
|
|
// "double tap to collapse".
|
|
expect(semantics.hintOverrides!.onTapHint, localizations.expansionTileExpandedTapHint);
|
|
handle.dispose();
|
|
});
|
|
|
|
testWidgets(
|
|
'Semantics hint for iOS and macOS',
|
|
(WidgetTester tester) async {
|
|
final controller = ExpansibleController();
|
|
addTearDown(controller.dispose);
|
|
final SemanticsHandle handle = tester.ensureSemantics();
|
|
const localizations = DefaultCupertinoLocalizations();
|
|
|
|
await tester.pumpWidget(
|
|
CupertinoApp(
|
|
home: Column(
|
|
children: <Widget>[
|
|
const CupertinoExpansionTile(
|
|
title: Text('First Expansion Tile'),
|
|
child: SizedBox(height: 100, width: 100),
|
|
),
|
|
CupertinoExpansionTile(
|
|
controller: controller,
|
|
title: const Text('Second Expansion Tile'),
|
|
child: const SizedBox(height: 100, width: 100),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
controller.expand();
|
|
await tester.pumpAndSettle();
|
|
|
|
SemanticsNode semantics = tester.getSemantics(
|
|
find
|
|
.ancestor(of: find.byType(CupertinoListTile).first, matching: find.byType(Semantics))
|
|
.first,
|
|
);
|
|
|
|
expect(semantics, isNotNull);
|
|
expect(
|
|
semantics.hint,
|
|
'${localizations.expandedHint}\n ${localizations.expansionTileCollapsedHint}',
|
|
);
|
|
|
|
semantics = tester.getSemantics(
|
|
find
|
|
.ancestor(of: find.byType(CupertinoListTile).last, matching: find.byType(Semantics))
|
|
.first,
|
|
);
|
|
|
|
expect(semantics, isNotNull);
|
|
expect(
|
|
semantics.hint,
|
|
'${localizations.collapsedHint}\n ${localizations.expansionTileExpandedHint}',
|
|
);
|
|
handle.dispose();
|
|
},
|
|
variant: const TargetPlatformVariant(<TargetPlatform>{
|
|
TargetPlatform.iOS,
|
|
TargetPlatform.macOS,
|
|
}),
|
|
);
|
|
|
|
testWidgets('CupertinoExpansionTile does not crash at zero area', (WidgetTester tester) async {
|
|
tester.view.physicalSize = Size.zero;
|
|
final controller = ExpansibleController();
|
|
addTearDown(tester.view.reset);
|
|
addTearDown(controller.dispose);
|
|
await tester.pumpWidget(
|
|
CupertinoApp(
|
|
home: Center(
|
|
child: CupertinoExpansionTile(
|
|
controller: controller,
|
|
title: const Text('X'),
|
|
child: const Text('Y'),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
expect(tester.getSize(find.byType(CupertinoExpansionTile)), Size.zero);
|
|
controller.expand();
|
|
await tester.pumpAndSettle();
|
|
});
|
|
}
|