text_test.dart 11.5 KB
Newer Older
1 2 3 4
// 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.

5
import 'package:flutter/gestures.dart';
6 7
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
8
import 'package:flutter/foundation.dart';
9

10
import '../rendering/mock_canvas.dart';
11 12
import 'semantics_tester.dart';

13 14
void main() {
  testWidgets('Text respects media query', (WidgetTester tester) async {
15
    await tester.pumpWidget(const MediaQuery(
16 17
      data: MediaQueryData(textScaleFactor: 1.3),
      child: Center(
18 19
        child: Text('Hello', textDirection: TextDirection.ltr),
      ),
20 21 22 23
    ));

    RichText text = tester.firstWidget(find.byType(RichText));
    expect(text, isNotNull);
24
    expect(text.textScaleFactor, 1.3);
25

26
    await tester.pumpWidget(const Center(
27
      child: Text('Hello', textDirection: TextDirection.ltr),
28 29 30 31 32
    ));

    text = tester.firstWidget(find.byType(RichText));
    expect(text, isNotNull);
    expect(text.textScaleFactor, 1.0);
33 34 35 36
  });

  testWidgets('Text respects textScaleFactor with default font size', (WidgetTester tester) async {
    await tester.pumpWidget(
37
      const Center(child: Text('Hello', textDirection: TextDirection.ltr))
38 39 40 41 42 43 44 45 46 47
    );

    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(
48
      child: Text('Hello', textScaleFactor: 1.5, textDirection: TextDirection.ltr),
49 50 51 52 53 54 55 56 57 58 59 60
    ));

    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(
61
      child: Text('Hello',
62
        style: TextStyle(fontSize: 20.0), textDirection: TextDirection.ltr),
63 64 65 66 67 68 69 70
    ));

    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));
71

72
    await tester.pumpWidget(const Center(
73 74
      child: Text('Hello',
        style: TextStyle(fontSize: 20.0),
75
        textScaleFactor: 1.3,
76
        textDirection: TextDirection.ltr),
77 78 79 80
    ));

    text = tester.firstWidget(find.byType(RichText));
    expect(text, isNotNull);
81 82 83 84
    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));
85
  });
86 87 88 89 90 91 92

  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 '));
  });
93 94 95 96

  testWidgets('Text can be created from TextSpans and uses defaultTextStyle', (WidgetTester tester) async {
    await tester.pumpWidget(
      const DefaultTextStyle(
97
        style: TextStyle(
98 99
          fontSize: 20.0,
        ),
100 101
        child: Text.rich(
          TextSpan(
102
            text: 'Hello',
103 104 105
            children: <TextSpan>[
              TextSpan(text: ' beautiful ', style: TextStyle(fontStyle: FontStyle.italic)),
              TextSpan(text: 'world', style: TextStyle(fontWeight: FontWeight.bold)),
106 107 108 109 110 111 112 113 114 115 116
            ],
          ),
          textDirection: TextDirection.ltr,
        ),
      ),
    );

    final RichText text = tester.firstWidget(find.byType(RichText));
    expect(text, isNotNull);
    expect(text.text.style.fontSize, 20.0);
  });
117 118

  testWidgets('semanticsLabel can override text label', (WidgetTester tester) async {
119
    final SemanticsTester semantics = SemanticsTester(tester);
120 121 122
    await tester.pumpWidget(
      const Text('\$\$', semanticsLabel: 'Double dollars', textDirection: TextDirection.ltr)
    );
123
    final TestSemantics expectedSemantics = TestSemantics.root(
124
      children: <TestSemantics>[
125
        TestSemantics.rootChild(
126 127 128 129 130 131 132 133 134 135
          label: 'Double dollars',
          textDirection: TextDirection.ltr,
        ),
      ],
    );
    expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreId: true, ignoreRect: true));

    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.ltr,
136
        child: Text('\$\$', semanticsLabel: 'Double dollars')),
137 138 139 140 141
    );

    expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreId: true, ignoreRect: true));
    semantics.dispose();
  });
142 143

  testWidgets('recognizers split semantic node', (WidgetTester tester) async {
144
    final SemanticsTester semantics = SemanticsTester(tester);
145 146
    const TextStyle textStyle = TextStyle(fontFamily: 'Ahem');
    await tester.pumpWidget(
147 148
      Text.rich(
        TextSpan(
149 150
          children: <TextSpan>[
            const TextSpan(text: 'hello '),
151
            TextSpan(text: 'world', recognizer: TapGestureRecognizer()..onTap = () { }),
152 153 154 155 156 157 158 159
            const TextSpan(text: ' this is a '),
            const TextSpan(text: 'cat-astrophe'),
          ],
          style: textStyle,
        ),
        textDirection: TextDirection.ltr,
      ),
    );
160
    final TestSemantics expectedSemantics = TestSemantics.root(
161
      children: <TestSemantics>[
162
        TestSemantics.rootChild(
163
          children: <TestSemantics>[
164
            TestSemantics(
165 166 167
              label: 'hello ',
              textDirection: TextDirection.ltr,
            ),
168
            TestSemantics(
169 170 171 172 173 174
              label: 'world',
              textDirection: TextDirection.ltr,
              actions: <SemanticsAction>[
                SemanticsAction.tap,
              ],
            ),
175
            TestSemantics(
176 177
              label: ' this is a cat-astrophe',
              textDirection: TextDirection.ltr,
178
            ),
179 180 181 182
          ],
        ),
      ],
    );
183
    expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreId: true, ignoreRect: true));
184 185 186 187
    semantics.dispose();
  });

  testWidgets('recognizers split semantic node - bidi', (WidgetTester tester) async {
188
    final SemanticsTester semantics = SemanticsTester(tester);
189 190
    const TextStyle textStyle = TextStyle(fontFamily: 'Ahem');
    await tester.pumpWidget(
191 192
      RichText(
        text: TextSpan(
193 194 195
          style: textStyle,
          children: <TextSpan>[
            const TextSpan(text: 'hello world${Unicode.RLE}${Unicode.RLO} '),
196
            TextSpan(text: 'BOY', recognizer: LongPressGestureRecognizer()..onLongPress = () { }),
197
            const TextSpan(text: ' HOW DO${Unicode.PDF} you ${Unicode.RLO} DO '),
198
            TextSpan(text: 'SIR', recognizer: TapGestureRecognizer()..onTap = () { }),
199 200 201 202 203 204 205 206
            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
207
    final TestSemantics expectedSemantics = TestSemantics.root(
208
      children: <TestSemantics>[
209 210
        TestSemantics.rootChild(
          rect: Rect.fromLTRB(0.0, 0.0, 800.0, 600.0),
211
          children: <TestSemantics>[
212 213
            TestSemantics(
              rect: Rect.fromLTRB(-4.0, -4.0, 480.0, 18.0),
214 215 216
              label: 'hello world ',
              textDirection: TextDirection.ltr, // text direction is declared as LTR.
            ),
217 218
            TestSemantics(
              rect: Rect.fromLTRB(150.0, -4.0, 200.0, 18.0),
219 220 221 222 223 224
              label: 'RIS',
              textDirection: TextDirection.rtl,  // in the last string we switched to RTL using RLE.
              actions: <SemanticsAction>[
                SemanticsAction.tap,
              ],
            ),
225 226
            TestSemantics(
              rect: Rect.fromLTRB(192.0, -4.0, 424.0, 18.0),
227 228 229
              label: ' OD you OD WOH ', // Still RTL.
              textDirection: TextDirection.rtl,
            ),
230 231
            TestSemantics(
              rect: Rect.fromLTRB(416.0, -4.0, 466.0, 18.0),
232 233 234 235 236 237
              label: 'YOB',
              textDirection: TextDirection.rtl, // Still RTL.
              actions: <SemanticsAction>[
                SemanticsAction.longPress,
              ],
            ),
238 239
            TestSemantics(
              rect: Rect.fromLTRB(472.0, -4.0, 606.0, 18.0),
240 241 242 243 244 245 246 247 248 249
              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.
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300


  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()));
  });
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320

  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()));
  });
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
}

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,
          ),
        ),
      ),
    ),
  );
339
}