blob: d91fb94f1356a243eff82d8fa2e07da5a345627f [file] [log] [blame]
// Copyright 2016 The Chromium 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 'package:flutter/gestures.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/foundation.dart';
import '../rendering/mock_canvas.dart';
import 'semantics_tester.dart';
void main() {
testWidgets('Text respects media query', (WidgetTester tester) async {
await tester.pumpWidget(const MediaQuery(
data: MediaQueryData(textScaleFactor: 1.3),
child: Center(
child: Text('Hello', textDirection: TextDirection.ltr),
),
));
RichText text = tester.firstWidget(find.byType(RichText));
expect(text, isNotNull);
expect(text.textScaleFactor, 1.3);
await tester.pumpWidget(const Center(
child: Text('Hello', textDirection: TextDirection.ltr),
));
text = tester.firstWidget(find.byType(RichText));
expect(text, isNotNull);
expect(text.textScaleFactor, 1.0);
});
testWidgets('Text respects textScaleFactor with default font size', (WidgetTester tester) async {
await tester.pumpWidget(
const Center(child: Text('Hello', textDirection: TextDirection.ltr))
);
RichText text = tester.firstWidget(find.byType(RichText));
expect(text, isNotNull);
expect(text.textScaleFactor, 1.0);
final Size baseSize = tester.getSize(find.byType(RichText));
expect(baseSize.width, equals(70.0));
expect(baseSize.height, equals(14.0));
await tester.pumpWidget(const Center(
child: Text('Hello', textScaleFactor: 1.5, textDirection: TextDirection.ltr),
));
text = tester.firstWidget(find.byType(RichText));
expect(text, isNotNull);
expect(text.textScaleFactor, 1.5);
final Size largeSize = tester.getSize(find.byType(RichText));
expect(largeSize.width, 105.0);
expect(largeSize.height, equals(21.0));
});
testWidgets('Text respects textScaleFactor with explicit font size', (WidgetTester tester) async {
await tester.pumpWidget(const Center(
child: Text('Hello',
style: TextStyle(fontSize: 20.0), textDirection: TextDirection.ltr),
));
RichText text = tester.firstWidget(find.byType(RichText));
expect(text, isNotNull);
expect(text.textScaleFactor, 1.0);
final Size baseSize = tester.getSize(find.byType(RichText));
expect(baseSize.width, equals(100.0));
expect(baseSize.height, equals(20.0));
await tester.pumpWidget(const Center(
child: Text('Hello',
style: TextStyle(fontSize: 20.0),
textScaleFactor: 1.3,
textDirection: TextDirection.ltr),
));
text = tester.firstWidget(find.byType(RichText));
expect(text, isNotNull);
expect(text.textScaleFactor, 1.3);
final Size largeSize = tester.getSize(find.byType(RichText));
expect(largeSize.width, anyOf(131.0, 130.0));
expect(largeSize.height, equals(26.0));
});
testWidgets('Text throws a nice error message if there\'s no Directionality', (WidgetTester tester) async {
await tester.pumpWidget(const Text('Hello'));
final String message = tester.takeException().toString();
expect(message, contains('Directionality'));
expect(message, contains(' Text '));
});
testWidgets('Text can be created from TextSpans and uses defaultTextStyle', (WidgetTester tester) async {
await tester.pumpWidget(
const DefaultTextStyle(
style: TextStyle(
fontSize: 20.0,
),
child: Text.rich(
TextSpan(
text: 'Hello',
children: <TextSpan>[
TextSpan(text: ' beautiful ', style: TextStyle(fontStyle: FontStyle.italic)),
TextSpan(text: 'world', style: TextStyle(fontWeight: FontWeight.bold)),
],
),
textDirection: TextDirection.ltr,
),
),
);
final RichText text = tester.firstWidget(find.byType(RichText));
expect(text, isNotNull);
expect(text.text.style.fontSize, 20.0);
});
testWidgets('semanticsLabel can override text label', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(
const Text('\$\$', semanticsLabel: 'Double dollars', textDirection: TextDirection.ltr)
);
final TestSemantics expectedSemantics = TestSemantics.root(
children: <TestSemantics>[
TestSemantics.rootChild(
label: 'Double dollars',
textDirection: TextDirection.ltr,
),
],
);
expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreId: true, ignoreRect: true));
await tester.pumpWidget(
const Directionality(
textDirection: TextDirection.ltr,
child: Text('\$\$', semanticsLabel: 'Double dollars')),
);
expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreId: true, ignoreRect: true));
semantics.dispose();
});
testWidgets('recognizers split semantic node', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
const TextStyle textStyle = TextStyle(fontFamily: 'Ahem');
await tester.pumpWidget(
Text.rich(
TextSpan(
children: <TextSpan>[
const TextSpan(text: 'hello '),
TextSpan(text: 'world', recognizer: TapGestureRecognizer()..onTap = () { }),
const TextSpan(text: ' this is a '),
const TextSpan(text: 'cat-astrophe'),
],
style: textStyle,
),
textDirection: TextDirection.ltr,
),
);
final TestSemantics expectedSemantics = TestSemantics.root(
children: <TestSemantics>[
TestSemantics.rootChild(
children: <TestSemantics>[
TestSemantics(
label: 'hello ',
textDirection: TextDirection.ltr,
),
TestSemantics(
label: 'world',
textDirection: TextDirection.ltr,
actions: <SemanticsAction>[
SemanticsAction.tap,
],
),
TestSemantics(
label: ' this is a cat-astrophe',
textDirection: TextDirection.ltr,
),
],
),
],
);
expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreId: true, ignoreRect: true));
semantics.dispose();
});
testWidgets('recognizers split semantic nodes with text span labels', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
const TextStyle textStyle = TextStyle(fontFamily: 'Ahem');
await tester.pumpWidget(
Text.rich(
TextSpan(
children: <TextSpan>[
const TextSpan(text: 'hello '),
TextSpan(text: 'world', recognizer: TapGestureRecognizer()..onTap = () { }),
const TextSpan(text: ' this is a '),
const TextSpan(text: 'cat-astrophe', semanticsLabel: 'regrettable event'),
],
style: textStyle,
),
textDirection: TextDirection.ltr,
),
);
final TestSemantics expectedSemantics = TestSemantics.root(
children: <TestSemantics>[
TestSemantics.rootChild(
children: <TestSemantics>[
TestSemantics(
label: 'hello ',
textDirection: TextDirection.ltr,
),
TestSemantics(
label: 'world',
textDirection: TextDirection.ltr,
actions: <SemanticsAction>[
SemanticsAction.tap,
],
),
TestSemantics(
label: ' regrettable event',
textDirection: TextDirection.ltr,
),
],
),
],
);
expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreId: true, ignoreRect: true));
semantics.dispose();
});
testWidgets('recognizers split semantic node - bidi', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
const TextStyle textStyle = TextStyle(fontFamily: 'Ahem');
await tester.pumpWidget(
RichText(
text: TextSpan(
style: textStyle,
children: <TextSpan>[
const TextSpan(text: 'hello world${Unicode.RLE}${Unicode.RLO} '),
TextSpan(text: 'BOY', recognizer: LongPressGestureRecognizer()..onLongPress = () { }),
const TextSpan(text: ' HOW DO${Unicode.PDF} you ${Unicode.RLO} DO '),
TextSpan(text: 'SIR', recognizer: TapGestureRecognizer()..onTap = () { }),
const TextSpan(text: '${Unicode.PDF}${Unicode.PDF} good bye'),
],
),
textDirection: TextDirection.ltr,
)
);
// The expected visual order of the text is:
// hello world RIS OD you OD WOH YOB good bye
final TestSemantics expectedSemantics = TestSemantics.root(
children: <TestSemantics>[
TestSemantics.rootChild(
rect: Rect.fromLTRB(0.0, 0.0, 800.0, 600.0),
children: <TestSemantics>[
TestSemantics(
rect: Rect.fromLTRB(-4.0, -4.0, 480.0, 18.0),
label: 'hello world ',
textDirection: TextDirection.ltr, // text direction is declared as LTR.
),
TestSemantics(
rect: Rect.fromLTRB(150.0, -4.0, 200.0, 18.0),
label: 'RIS',
textDirection: TextDirection.rtl, // in the last string we switched to RTL using RLE.
actions: <SemanticsAction>[
SemanticsAction.tap,
],
),
TestSemantics(
rect: Rect.fromLTRB(192.0, -4.0, 424.0, 18.0),
label: ' OD you OD WOH ', // Still RTL.
textDirection: TextDirection.rtl,
),
TestSemantics(
rect: Rect.fromLTRB(416.0, -4.0, 466.0, 18.0),
label: 'YOB',
textDirection: TextDirection.rtl, // Still RTL.
actions: <SemanticsAction>[
SemanticsAction.longPress,
],
),
TestSemantics(
rect: Rect.fromLTRB(472.0, -4.0, 606.0, 18.0),
label: ' good bye',
textDirection: TextDirection.rtl, // Begin as RTL but pop to LTR.
),
],
),
],
);
expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreId: true));
semantics.dispose();
}, skip: true); // TODO(jonahwilliams): correct once https://github.com/flutter/flutter/issues/20891 is resolved.
testWidgets('Overflow is clipping correctly - short text with overflow: clip', (WidgetTester tester) async {
await _pumpTextWidget(
tester: tester,
overflow: TextOverflow.clip,
text: 'Hi',
);
expect(find.byType(Text), isNot(paints..clipRect()));
});
testWidgets('Overflow is clipping correctly - long text with overflow: ellipsis', (WidgetTester tester) async {
await _pumpTextWidget(
tester: tester,
overflow: TextOverflow.ellipsis,
text: 'a long long long long text, should be clip',
);
expect(find.byType(Text), paints..clipRect(rect: Rect.fromLTWH(0, 0, 50, 50)));
});
testWidgets('Overflow is clipping correctly - short text with overflow: ellipsis', (WidgetTester tester) async {
await _pumpTextWidget(
tester: tester,
overflow: TextOverflow.ellipsis,
text: 'Hi',
);
expect(find.byType(Text), isNot(paints..clipRect()));
});
testWidgets('Overflow is clipping correctly - long text with overflow: fade', (WidgetTester tester) async {
await _pumpTextWidget(
tester: tester,
overflow: TextOverflow.fade,
text: 'a long long long long text, should be clip',
);
expect(find.byType(Text), paints..clipRect(rect: Rect.fromLTWH(0, 0, 50, 50)));
});
testWidgets('Overflow is clipping correctly - short text with overflow: fade', (WidgetTester tester) async {
await _pumpTextWidget(
tester: tester,
overflow: TextOverflow.fade,
text: 'Hi',
);
expect(find.byType(Text), isNot(paints..clipRect()));
});
testWidgets('Overflow is clipping correctly - long text with overflow: visible', (WidgetTester tester) async {
await _pumpTextWidget(
tester: tester,
overflow: TextOverflow.visible,
text: 'a long long long long text, should be clip',
);
expect(find.byType(Text), isNot(paints..clipRect()));
});
testWidgets('Overflow is clipping correctly - short text with overflow: visible', (WidgetTester tester) async {
await _pumpTextWidget(
tester: tester,
overflow: TextOverflow.visible,
text: 'Hi',
);
expect(find.byType(Text), isNot(paints..clipRect()));
});
}
Future<void> _pumpTextWidget({ WidgetTester tester, String text, TextOverflow overflow }) {
return tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Center(
child: Container(
width: 50.0,
height: 50.0,
child: Text(
text,
overflow: overflow,
),
),
),
),
);
}