blob: e98bf6c18ccc710f366e0e1d6ba239c5cc2b3f5e [file] [log] [blame]
// 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.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
Future<void> pumpContainer(WidgetTester tester, Widget child) async {
await tester.pumpWidget(MaterialApp(home: child));
}
testWidgets('updates its registrar and delegate based on the number of selectables', (WidgetTester tester) async {
final TestSelectionRegistrar registrar = TestSelectionRegistrar();
final TestContainerDelegate delegate = TestContainerDelegate();
addTearDown(delegate.dispose);
await pumpContainer(
tester,
SelectionContainer(
registrar: registrar,
delegate: delegate,
child: const Column(
children: <Widget>[
Text('column1', textDirection: TextDirection.ltr),
Text('column2', textDirection: TextDirection.ltr),
Text('column3', textDirection: TextDirection.ltr),
],
),
),
);
await tester.pumpAndSettle();
expect(registrar.selectables.length, 1);
expect(delegate.selectables.length, 3);
});
testWidgets('disabled container', (WidgetTester tester) async {
final TestSelectionRegistrar registrar = TestSelectionRegistrar();
final TestContainerDelegate delegate = TestContainerDelegate();
addTearDown(delegate.dispose);
await pumpContainer(
tester,
SelectionContainer(
registrar: registrar,
delegate: delegate,
child: const SelectionContainer.disabled(
child: Column(
children: <Widget>[
Text('column1', textDirection: TextDirection.ltr),
Text('column2', textDirection: TextDirection.ltr),
Text('column3', textDirection: TextDirection.ltr),
],
),
),
),
);
expect(registrar.selectables.length, 0);
expect(delegate.selectables.length, 0);
});
testWidgets('Swapping out container delegate does not crash', (WidgetTester tester) async {
final TestSelectionRegistrar registrar = TestSelectionRegistrar();
final TestContainerDelegate delegate = TestContainerDelegate();
addTearDown(delegate.dispose);
final TestContainerDelegate childDelegate = TestContainerDelegate();
addTearDown(childDelegate.dispose);
await pumpContainer(
tester,
SelectionContainer(
registrar: registrar,
delegate: delegate,
child: Builder(
builder: (BuildContext context) {
return SelectionContainer(
registrar: SelectionContainer.maybeOf(context),
delegate: childDelegate,
child: const Text('dummy'),
);
},
),
),
);
await tester.pumpAndSettle();
expect(registrar.selectables.length, 1);
expect(delegate.value.hasContent, isTrue);
final TestContainerDelegate newDelegate = TestContainerDelegate();
addTearDown(newDelegate.dispose);
await pumpContainer(
tester,
SelectionContainer(
registrar: registrar,
delegate: delegate,
child: Builder(
builder: (BuildContext context) {
return SelectionContainer(
registrar: SelectionContainer.maybeOf(context),
delegate: newDelegate,
child: const Text('dummy'),
);
},
)
),
);
await tester.pumpAndSettle();
expect(registrar.selectables.length, 1);
expect(delegate.value.hasContent, isTrue);
expect(tester.takeException(), isNull);
});
testWidgets('Can update within one frame', (WidgetTester tester) async {
final TestSelectionRegistrar registrar = TestSelectionRegistrar();
final TestContainerDelegate delegate = TestContainerDelegate();
addTearDown(delegate.dispose);
final TestContainerDelegate childDelegate = TestContainerDelegate();
addTearDown(childDelegate.dispose);
await pumpContainer(
tester,
SelectionContainer(
registrar: registrar,
delegate: delegate,
child: Builder(
builder: (BuildContext context) {
return SelectionContainer(
registrar: SelectionContainer.maybeOf(context),
delegate: childDelegate,
child: const Text('dummy'),// The [Text] widget has an internal [SelectionContainer].
);
},
),
),
);
await tester.pump();
// Should finish update after flushing the micro tasks.
await tester.idle();
expect(registrar.selectables.length, 1);
expect(delegate.value.hasContent, isTrue);
});
testWidgets('selection container registers itself if there is a selectable child', (WidgetTester tester) async {
final TestSelectionRegistrar registrar = TestSelectionRegistrar();
final TestContainerDelegate delegate = TestContainerDelegate();
addTearDown(delegate.dispose);
await pumpContainer(
tester,
SelectionContainer(
registrar: registrar,
delegate: delegate,
child: const Column(
),
),
);
expect(registrar.selectables.length, 0);
await pumpContainer(
tester,
SelectionContainer(
registrar: registrar,
delegate: delegate,
child: const Column(
children: <Widget>[
Text('column1', textDirection: TextDirection.ltr),
],
),
),
);
await tester.pumpAndSettle();
expect(registrar.selectables.length, 1);
await pumpContainer(
tester,
SelectionContainer(
registrar: registrar,
delegate: delegate,
child: const Column(
),
),
);
await tester.pumpAndSettle();
expect(registrar.selectables.length, 0);
});
testWidgets('selection container gets registrar from context if not provided', (WidgetTester tester) async {
final TestSelectionRegistrar registrar = TestSelectionRegistrar();
final TestContainerDelegate delegate = TestContainerDelegate();
addTearDown(delegate.dispose);
await pumpContainer(
tester,
SelectionRegistrarScope(
registrar: registrar,
child: SelectionContainer(
delegate: delegate,
child: const Column(
children: <Widget>[
Text('column1', textDirection: TextDirection.ltr),
],
),
),
),
);
await tester.pumpAndSettle();
expect(registrar.selectables.length, 1);
});
}
class TestContainerDelegate extends MultiSelectableSelectionContainerDelegate {
@override
SelectionResult dispatchSelectionEventToChild(Selectable selectable, SelectionEvent event) {
throw UnimplementedError();
}
@override
void ensureChildUpdated(Selectable selectable) {
throw UnimplementedError();
}
}
class TestSelectionRegistrar extends SelectionRegistrar {
final Set<Selectable> selectables = <Selectable>{};
@override
void add(Selectable selectable) => selectables.add(selectable);
@override
void remove(Selectable selectable) => selectables.remove(selectable);
}