text_formatter_test.dart 7.31 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';

void main() {
  TextEditingValue testOldValue;
  TextEditingValue testNewValue;

  test('withFunction wraps formatting function', () {
    testOldValue = const TextEditingValue();
    testNewValue = const TextEditingValue();

    TextEditingValue calledOldValue;
    TextEditingValue calledNewValue;

    final TextInputFormatter formatterUnderTest = TextInputFormatter.withFunction(
      (TextEditingValue oldValue, TextEditingValue newValue) {
        calledOldValue = oldValue;
        calledNewValue = newValue;
24
        return null;
25 26 27 28 29 30 31 32 33 34 35 36
      }
    );

    formatterUnderTest.formatEditUpdate(testOldValue, testNewValue);

    expect(calledOldValue, equals(testOldValue));
    expect(calledNewValue, equals(testNewValue));
  });

  group('test provided formatters', () {
    setUp(() {
      // a1b(2c3
37
      // d4)e5f6
38 39 40
      // where the parentheses are the selection range.
      testNewValue = const TextEditingValue(
        text: 'a1b2c3\nd4e5f6',
41
        selection: TextSelection(
42 43 44 45 46 47 48
          baseOffset: 3,
          extentOffset: 9,
        ),
      );
    });

    test('test blacklisting formatter', () {
49
      final TextEditingValue actualValue =
50
          BlacklistingTextInputFormatter(RegExp(r'[a-z]'))
51 52 53 54
              .formatEditUpdate(testOldValue, testNewValue);

      // Expecting
      // 1(23
55
      // 4)56
56 57
      expect(actualValue, const TextEditingValue(
        text: '123\n456',
58
        selection: TextSelection(
59 60 61 62 63 64 65
          baseOffset: 1,
          extentOffset: 5,
        ),
      ));
    });

    test('test single line formatter', () {
66
      final TextEditingValue actualValue =
67 68 69 70
          BlacklistingTextInputFormatter.singleLineFormatter
              .formatEditUpdate(testOldValue, testNewValue);

      // Expecting
71
      // a1b(2c3d4)e5f6
72 73
      expect(actualValue, const TextEditingValue(
        text: 'a1b2c3d4e5f6',
74
        selection: TextSelection(
75 76 77 78 79 80 81
          baseOffset: 3,
          extentOffset: 8,
        ),
      ));
    });

    test('test whitelisting formatter', () {
82
      final TextEditingValue actualValue =
83
          WhitelistingTextInputFormatter(RegExp(r'[a-c]'))
84 85 86 87 88 89
              .formatEditUpdate(testOldValue, testNewValue);

      // Expecting
      // ab(c)
      expect(actualValue, const TextEditingValue(
        text: 'abc',
90
        selection: TextSelection(
91 92 93 94 95 96 97
          baseOffset: 2,
          extentOffset: 3,
        ),
      ));
    });

    test('test digits only formatter', () {
98
      final TextEditingValue actualValue =
99 100 101 102
          WhitelistingTextInputFormatter.digitsOnly
              .formatEditUpdate(testOldValue, testNewValue);

      // Expecting
103
      // 1(234)56
104 105
      expect(actualValue, const TextEditingValue(
        text: '123456',
106
        selection: TextSelection(
107 108 109 110 111
          baseOffset: 1,
          extentOffset: 4,
        ),
      ));
    });
112 113 114

    test('test length limiting formatter', () {
      final TextEditingValue actualValue =
115
      LengthLimitingTextInputFormatter(6)
116 117 118 119 120 121
          .formatEditUpdate(testOldValue, testNewValue);

      // Expecting
      // a1b(2c3)
      expect(actualValue, const TextEditingValue(
        text: 'a1b2c3',
122
        selection: TextSelection(
123 124 125 126 127 128 129 130 131
          baseOffset: 3,
          extentOffset: 6,
        ),
      ));
    });

    test('test length limiting formatter with zero-length string', () {
      testNewValue = const TextEditingValue(
        text: '',
132
        selection: TextSelection(
133 134 135 136 137 138
          baseOffset: 0,
          extentOffset: 0,
        ),
      );

      final TextEditingValue actualValue =
139
      LengthLimitingTextInputFormatter(1)
140 141 142 143 144
        .formatEditUpdate(testOldValue, testNewValue);

      // Expecting the empty string.
      expect(actualValue, const TextEditingValue(
        text: '',
145
        selection: TextSelection(
146 147 148 149 150 151 152 153 154
          baseOffset: 0,
          extentOffset: 0,
        ),
      ));
    });

    test('test length limiting formatter with non-BMP Unicode scalar values', () {
      testNewValue = const TextEditingValue(
        text: '\u{1f984}\u{1f984}\u{1f984}\u{1f984}', // Unicode U+1f984 (UNICORN FACE)
155
        selection: TextSelection(
156 157 158 159 160 161
          baseOffset: 4,
          extentOffset: 4,
        ),
      );

      final TextEditingValue actualValue =
162
      LengthLimitingTextInputFormatter(2)
163 164 165 166 167
        .formatEditUpdate(testOldValue, testNewValue);

      // Expecting two runes.
      expect(actualValue, const TextEditingValue(
        text: '\u{1f984}\u{1f984}',
168
        selection: TextSelection(
169 170 171 172 173 174 175 176
          baseOffset: 2,
          extentOffset: 2,
        ),
      ));
    });


    test('test length limiting formatter with complex Unicode characters', () {
177
      // TODO(gspencer): Test additional strings. We can do this once the
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
      // formatter supports Unicode grapheme clusters.
      //
      // A formatter with max length 1 should accept:
      //  - The '\u{1F3F3}\u{FE0F}\u{200D}\u{1F308}' sequence (flag followed by
      //    a variation selector, a zero-width joiner, and a rainbow to make a rainbow
      //    flag).
      //  - The sequence '\u{0058}\u{0346}\u{0361}\u{035E}\u{032A}\u{031C}\u{0333}\u{0326}\u{031D}\u{0332}'
      //    (Latin X with many composed characters).
      //
      // A formatter should not count as a character:
      //   * The '\u{0000}\u{FEFF}' sequence. (NULL followed by zero-width no-break space).
      //
      // A formatter with max length 1 should truncate this to one character:
      //   * The '\u{1F3F3}\u{FE0F}\u{1F308}' sequence (flag with ignored variation
      //     selector followed by rainbow, should truncate to just flag).

      // The U+1F984 U+0020 sequence: Unicorn face followed by a space should
      // yield only the unicorn face.
      testNewValue = const TextEditingValue(
        text: '\u{1F984}\u{0020}',
198
        selection: TextSelection(
199 200 201 202
          baseOffset: 1,
          extentOffset: 1,
        ),
      );
203
      TextEditingValue actualValue = LengthLimitingTextInputFormatter(1).formatEditUpdate(testOldValue, testNewValue);
204 205
      expect(actualValue, const TextEditingValue(
        text: '\u{1F984}',
206
        selection: TextSelection(
207 208 209 210 211 212 213 214 215
          baseOffset: 1,
          extentOffset: 1,
        ),
      ));

      // The U+0058 U+0059 sequence: Latin X followed by Latin Y, should yield
      // Latin X.
      testNewValue = const TextEditingValue(
        text: '\u{0058}\u{0059}',
216
        selection: TextSelection(
217 218 219 220
          baseOffset: 1,
          extentOffset: 1,
        ),
      );
221
      actualValue = LengthLimitingTextInputFormatter(1).formatEditUpdate(testOldValue, testNewValue);
222 223
      expect(actualValue, const TextEditingValue(
        text: '\u{0058}',
224
        selection: TextSelection(
225 226 227 228 229 230 231 232 233
          baseOffset: 1,
          extentOffset: 1,
        ),
      ));
    });


    test('test length limiting formatter when selection is off the end', () {
      final TextEditingValue actualValue =
234
      LengthLimitingTextInputFormatter(2)
235 236 237 238 239 240
          .formatEditUpdate(testOldValue, testNewValue);

      // Expecting
      // a1()
      expect(actualValue, const TextEditingValue(
        text: 'a1',
241
        selection: TextSelection(
242 243 244 245 246 247
          baseOffset: 2,
          extentOffset: 2,
        ),
      ));
    });

248 249
  });
}