
- Bumps `vm_service` from `11.6.0` to `11.7.1` - Bumps `web` from `0.1.3-beta` to `0.1.4-beta` and adds it everywhere. - Moves `js` from `dependencies` to `dev_dependencies`
190 lines
5.5 KiB
Dart
190 lines
5.5 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.
|
|
|
|
@TestOn('browser') // This file contains web-only library.
|
|
library;
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:web/web.dart' as web;
|
|
|
|
extension on web.HTMLCollection {
|
|
Iterable<web.Element> get iterable => _genIterable(this);
|
|
}
|
|
extension on web.CSSRuleList {
|
|
Iterable<web.CSSRule> get iterable => _genIterable(this);
|
|
}
|
|
|
|
typedef ItemGetter<T> = T? Function(int index);
|
|
Iterable<T> _genIterable<T>(dynamic jsCollection) {
|
|
// ignore: avoid_dynamic_calls
|
|
return Iterable<T>.generate(jsCollection.length as int, (int index) => jsCollection.item(index) as T,);
|
|
}
|
|
|
|
void main() {
|
|
web.HTMLElement? element;
|
|
PlatformSelectableRegionContextMenu.debugOverrideRegisterViewFactory = (String viewType, Object Function(int viewId) fn, {bool isVisible = true}) {
|
|
element = fn(0) as web.HTMLElement;
|
|
// The element needs to be attached to the document body to receive mouse
|
|
// events.
|
|
web.document.body!.append(element);
|
|
};
|
|
// This force register the dom element.
|
|
PlatformSelectableRegionContextMenu(child: const Placeholder());
|
|
PlatformSelectableRegionContextMenu.debugOverrideRegisterViewFactory = null;
|
|
|
|
test('DOM element is set up correctly', () async {
|
|
expect(element, isNotNull);
|
|
expect(element!.style.width, '100%');
|
|
expect(element!.style.height, '100%');
|
|
expect(element!.classList.length, 1);
|
|
final String className = element!.className;
|
|
|
|
expect(web.document.head!.children.iterable, isNotEmpty);
|
|
bool foundStyle = false;
|
|
for (final web.Element element in web.document.head!.children.iterable) {
|
|
if (element.tagName != 'STYLE') {
|
|
continue;
|
|
}
|
|
final web.CSSRuleList? rules = (element as web.HTMLStyleElement).sheet?.rules;
|
|
if (rules != null) {
|
|
foundStyle = rules.iterable.any((web.CSSRule rule) => rule.cssText.contains(className));
|
|
}
|
|
if (foundStyle) {
|
|
break;
|
|
}
|
|
}
|
|
expect(foundStyle, isTrue);
|
|
});
|
|
|
|
testWidgets('right click can trigger select word', (WidgetTester tester) async {
|
|
final FocusNode focusNode = FocusNode();
|
|
final UniqueKey spy = UniqueKey();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: SelectableRegion(
|
|
focusNode: focusNode,
|
|
selectionControls: materialTextSelectionControls,
|
|
child: SelectionSpy(key: spy),
|
|
),
|
|
)
|
|
);
|
|
expect(element, isNotNull);
|
|
|
|
focusNode.requestFocus();
|
|
await tester.pump();
|
|
|
|
// Dispatch right click.
|
|
element!.dispatchEvent(
|
|
web.MouseEvent(
|
|
'mousedown',
|
|
web.MouseEventInit(
|
|
button: 2,
|
|
clientX: 200,
|
|
clientY: 300,
|
|
),
|
|
),
|
|
);
|
|
final RenderSelectionSpy renderSelectionSpy = tester.renderObject<RenderSelectionSpy>(find.byKey(spy));
|
|
expect(renderSelectionSpy.events, isNotEmpty);
|
|
|
|
SelectWordSelectionEvent? selectWordEvent;
|
|
for (final SelectionEvent event in renderSelectionSpy.events) {
|
|
if (event is SelectWordSelectionEvent) {
|
|
selectWordEvent = event;
|
|
break;
|
|
}
|
|
}
|
|
expect(selectWordEvent, isNotNull);
|
|
expect((selectWordEvent!.globalPosition.dx - 200).abs() < precisionErrorTolerance, isTrue);
|
|
expect((selectWordEvent.globalPosition.dy - 300).abs() < precisionErrorTolerance, isTrue);
|
|
});
|
|
}
|
|
|
|
class SelectionSpy extends LeafRenderObjectWidget {
|
|
const SelectionSpy({
|
|
super.key,
|
|
});
|
|
|
|
@override
|
|
RenderObject createRenderObject(BuildContext context) {
|
|
return RenderSelectionSpy(
|
|
SelectionContainer.maybeOf(context),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { }
|
|
}
|
|
|
|
class RenderSelectionSpy extends RenderProxyBox
|
|
with Selectable, SelectionRegistrant {
|
|
RenderSelectionSpy(
|
|
SelectionRegistrar? registrar,
|
|
) {
|
|
this.registrar = registrar;
|
|
}
|
|
|
|
final Set<VoidCallback> listeners = <VoidCallback>{};
|
|
List<SelectionEvent> events = <SelectionEvent>[];
|
|
|
|
@override
|
|
Size get size => _size;
|
|
Size _size = Size.zero;
|
|
|
|
@override
|
|
Size computeDryLayout(BoxConstraints constraints) {
|
|
_size = Size(constraints.maxWidth, constraints.maxHeight);
|
|
return _size;
|
|
}
|
|
|
|
@override
|
|
void addListener(VoidCallback listener) => listeners.add(listener);
|
|
|
|
@override
|
|
void removeListener(VoidCallback listener) => listeners.remove(listener);
|
|
|
|
@override
|
|
SelectionResult dispatchSelectionEvent(SelectionEvent event) {
|
|
events.add(event);
|
|
return SelectionResult.end;
|
|
}
|
|
|
|
@override
|
|
SelectedContent? getSelectedContent() {
|
|
return const SelectedContent(plainText: 'content');
|
|
}
|
|
|
|
@override
|
|
SelectionGeometry get value => _value;
|
|
SelectionGeometry _value = const SelectionGeometry(
|
|
hasContent: true,
|
|
status: SelectionStatus.uncollapsed,
|
|
startSelectionPoint: SelectionPoint(
|
|
localPosition: Offset.zero,
|
|
lineHeight: 0.0,
|
|
handleType: TextSelectionHandleType.left,
|
|
),
|
|
endSelectionPoint: SelectionPoint(
|
|
localPosition: Offset.zero,
|
|
lineHeight: 0.0,
|
|
handleType: TextSelectionHandleType.left,
|
|
),
|
|
);
|
|
set value(SelectionGeometry other) {
|
|
if (other == _value) {
|
|
return;
|
|
}
|
|
_value = other;
|
|
for (final VoidCallback callback in listeners) {
|
|
callback();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void pushHandleLayers(LayerLink? startHandle, LayerLink? endHandle) { }
|
|
}
|