// Copyright 2019 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 'dart:html'; import 'dart:typed_data'; import 'package:flutter_web_ui/ui.dart' as ui; import 'package:flutter_web_ui/src/engine.dart'; import 'package:flutter_web_test/flutter_web_test.dart'; const MethodCodec codec = JSONMethodCodec(); TextEditingElement editingElement; EditingState lastEditingState; final InputConfiguration singlelineConfig = InputConfiguration(inputType: InputType.text); final Map flutterSinglelineConfig = { 'inputType': { 'name': 'TextInputType.text', }, 'obscureText': false, }; final InputConfiguration multilineConfig = InputConfiguration(inputType: InputType.multiline); final Map flutterMultilineConfig = { 'inputType': { 'name': 'TextInputType.multiline', }, 'obscureText': false, }; void trackEditingState(EditingState editingState) { lastEditingState = editingState; } void main() { group('$TextEditingElement', () { setUp(() { editingElement = TextEditingElement(); }); tearDown(() { try { editingElement.disable(); } catch (e) { if (e is AssertionError) { // This is fine. It just means the test itself disabled the editing element. } else { rethrow; } } }); test('Creates element when enabled and removes it when disabled', () { expect( document.getElementsByTagName('input'), hasLength(0), ); // The focus initially is on the body. expect(document.activeElement, document.body); editingElement.enable(singlelineConfig, onChange: trackEditingState); expect( document.getElementsByTagName('input'), hasLength(1), ); final InputElement input = document.getElementsByTagName('input')[0]; // Now the editing element should have focus. expect(document.activeElement, input); expect(editingElement.domElement, input); editingElement.disable(); expect( document.getElementsByTagName('input'), hasLength(0), ); // The focus is back to the body. expect(document.activeElement, document.body); }); test('Can read editing state correctly', () { editingElement.enable(singlelineConfig, onChange: trackEditingState); final InputElement input = editingElement.domElement; input.value = 'foo bar'; input.dispatchEvent(Event.eventType('Event', 'input')); expect( lastEditingState, EditingState(text: 'foo bar', baseOffset: 7, extentOffset: 7), ); input.setSelectionRange(4, 6); document.dispatchEvent(Event.eventType('Event', 'selectionchange')); expect( lastEditingState, EditingState(text: 'foo bar', baseOffset: 4, extentOffset: 6), ); }); test('Can set editing state correctly', () { editingElement.enable(singlelineConfig, onChange: trackEditingState); editingElement.setEditingState( EditingState(text: 'foo bar baz', baseOffset: 2, extentOffset: 7)); checkInputEditingState(editingElement.domElement, 'foo bar baz', 2, 7); }); test('Re-acquires focus', () async { editingElement.enable(singlelineConfig, onChange: trackEditingState); expect(document.activeElement, editingElement.domElement); editingElement.domElement.blur(); // The focus remains on [editingElement.domElement]. expect(document.activeElement, editingElement.domElement); }); test('Multi-line mode also works', () { // The textarea element is created lazily. expect(document.getElementsByTagName('textarea'), hasLength(0)); editingElement.enable(multilineConfig, onChange: trackEditingState); expect(document.getElementsByTagName('textarea'), hasLength(1)); final TextAreaElement textarea = document.getElementsByTagName('textarea')[0]; // Now the textarea should have focus. expect(document.activeElement, textarea); expect(editingElement.domElement, textarea); textarea.value = 'foo\nbar'; textarea.dispatchEvent(Event.eventType('Event', 'input')); textarea.setSelectionRange(4, 6); textarea.dispatchEvent(Event.eventType('Event', 'selectionchange')); // Can read textarea state correctly (and preserves new lines). expect( lastEditingState, EditingState(text: 'foo\nbar', baseOffset: 4, extentOffset: 6), ); // Can set textarea state correctly (and preserves new lines). editingElement.setEditingState( EditingState(text: 'bar\nbaz', baseOffset: 2, extentOffset: 7)); checkTextAreaEditingState(textarea, 'bar\nbaz', 2, 7); // Re-acquires focus. textarea.blur(); expect(document.activeElement, textarea); editingElement.disable(); // The textarea should be cleaned up. expect(document.getElementsByTagName('textarea'), hasLength(0)); // The focus is back to the body. expect(document.activeElement, document.body); }); test('Same instance can be re-enabled with different config', () { // Make sure there's nothing in the DOM yet. expect(document.getElementsByTagName('input'), hasLength(0)); expect(document.getElementsByTagName('textarea'), hasLength(0)); // Use single-line config and expect an `` to be created. editingElement.enable(singlelineConfig, onChange: trackEditingState); expect(document.getElementsByTagName('input'), hasLength(1)); expect(document.getElementsByTagName('textarea'), hasLength(0)); // Disable and check that all DOM elements were removed. editingElement.disable(); expect(document.getElementsByTagName('input'), hasLength(0)); expect(document.getElementsByTagName('textarea'), hasLength(0)); // Use multi-line config and expect an `