blob: e9550ef9bca51f511a02ff95f6c182e205b40e37 [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/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'restoration.dart';
void main() {
group('UnmanagedRestorationScope', () {
testWidgets('makes bucket available to descendants', (WidgetTester tester) async {
final RestorationBucket bucket1 = RestorationBucket.empty(
restorationId: 'foo',
debugOwner: 'owner',
);
await tester.pumpWidget(
UnmanagedRestorationScope(
bucket: bucket1,
child: const BucketSpy(),
),
);
final BucketSpyState state = tester.state(find.byType(BucketSpy));
expect(state.bucket, bucket1);
// Notifies when bucket changes.
final RestorationBucket bucket2 = RestorationBucket.empty(
restorationId: 'foo2',
debugOwner: 'owner',
);
await tester.pumpWidget(
UnmanagedRestorationScope(
bucket: bucket2,
child: const BucketSpy(),
),
);
expect(state.bucket, bucket2);
});
testWidgets('null bucket disables restoration', (WidgetTester tester) async {
await tester.pumpWidget(
const UnmanagedRestorationScope(
child: BucketSpy(),
),
);
final BucketSpyState state = tester.state(find.byType(BucketSpy));
expect(state.bucket, isNull);
});
});
group('RestorationScope', () {
testWidgets('makes bucket available to descendants', (WidgetTester tester) async {
const String id = 'hello world 1234';
final MockRestorationManager manager = MockRestorationManager();
final Map<String, dynamic> rawData = <String, dynamic>{};
final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: rawData);
expect(rawData, isEmpty);
await tester.pumpWidget(
UnmanagedRestorationScope(
bucket: root,
child: const RestorationScope(
restorationId: id,
child: BucketSpy(),
),
),
);
manager.doSerialization();
final BucketSpyState state = tester.state(find.byType(BucketSpy));
expect(state.bucket!.restorationId, id);
expect((rawData[childrenMapKey] as Map<Object?, Object?>).containsKey(id), isTrue);
});
testWidgets('bucket for descendants contains data claimed from parent', (WidgetTester tester) async {
final MockRestorationManager manager = MockRestorationManager();
final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: _createRawDataSet());
await tester.pumpWidget(
UnmanagedRestorationScope(
bucket: root,
child: const RestorationScope(
restorationId: 'child1',
child: BucketSpy(),
),
),
);
manager.doSerialization();
final BucketSpyState state = tester.state(find.byType(BucketSpy));
expect(state.bucket!.restorationId, 'child1');
expect(state.bucket!.read<int>('foo'), 22);
});
testWidgets('renames existing bucket when new ID is provided', (WidgetTester tester) async {
final MockRestorationManager manager = MockRestorationManager();
final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: _createRawDataSet());
await tester.pumpWidget(
UnmanagedRestorationScope(
bucket: root,
child: const RestorationScope(
restorationId: 'child1',
child: BucketSpy(),
),
),
);
manager.doSerialization();
// Claimed existing bucket with data.
final BucketSpyState state = tester.state(find.byType(BucketSpy));
expect(state.bucket!.restorationId, 'child1');
expect(state.bucket!.read<int>('foo'), 22);
final RestorationBucket bucket = state.bucket!;
// Rename the existing bucket.
await tester.pumpWidget(
UnmanagedRestorationScope(
bucket: root,
child: const RestorationScope(
restorationId: 'something else',
child: BucketSpy(),
),
),
);
manager.doSerialization();
expect(state.bucket!.restorationId, 'something else');
expect(state.bucket!.read<int>('foo'), 22);
expect(state.bucket, same(bucket));
});
testWidgets('Disposing a scope removes its data', (WidgetTester tester) async {
final MockRestorationManager manager = MockRestorationManager();
final Map<String, dynamic> rawData = _createRawDataSet();
final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: rawData);
expect((rawData[childrenMapKey] as Map<String, dynamic>).containsKey('child1'), isTrue);
await tester.pumpWidget(
UnmanagedRestorationScope(
bucket: root,
child: const RestorationScope(
restorationId: 'child1',
child: BucketSpy(),
),
),
);
manager.doSerialization();
expect((rawData[childrenMapKey] as Map<String, dynamic>).containsKey('child1'), isTrue);
await tester.pumpWidget(
UnmanagedRestorationScope(
bucket: root,
child: Container(),
),
);
manager.doSerialization();
expect((rawData[childrenMapKey] as Map<String, dynamic>).containsKey('child1'), isFalse);
});
testWidgets('no bucket for descendants when id is null', (WidgetTester tester) async {
final MockRestorationManager manager = MockRestorationManager();
final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: <String, dynamic>{});
await tester.pumpWidget(
UnmanagedRestorationScope(
bucket: root,
child: const RestorationScope(
restorationId: null,
child: BucketSpy(),
),
),
);
final BucketSpyState state = tester.state(find.byType(BucketSpy));
expect(state.bucket, isNull);
// Change id to non-null.
await tester.pumpWidget(
UnmanagedRestorationScope(
bucket: root,
child: const RestorationScope(
restorationId: 'foo',
child: BucketSpy(),
),
),
);
manager.doSerialization();
expect(state.bucket, isNotNull);
expect(state.bucket!.restorationId, 'foo');
// Change id back to null.
await tester.pumpWidget(
UnmanagedRestorationScope(
bucket: root,
child: const RestorationScope(
restorationId: null,
child: BucketSpy(),
),
),
);
manager.doSerialization();
expect(state.bucket, isNull);
});
testWidgets('no bucket for descendants when scope is null', (WidgetTester tester) async {
final Key scopeKey = GlobalKey();
await tester.pumpWidget(
RestorationScope(
key: scopeKey,
restorationId: 'foo',
child: const BucketSpy(),
),
);
final BucketSpyState state = tester.state(find.byType(BucketSpy));
expect(state.bucket, isNull);
// Move it under a valid scope.
final MockRestorationManager manager = MockRestorationManager();
final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: <String, dynamic>{});
await tester.pumpWidget(
UnmanagedRestorationScope(
bucket: root,
child: RestorationScope(
key: scopeKey,
restorationId: 'foo',
child: const BucketSpy(),
),
),
);
manager.doSerialization();
expect(state.bucket, isNotNull);
expect(state.bucket!.restorationId, 'foo');
// Move out of scope again.
await tester.pumpWidget(
RestorationScope(
key: scopeKey,
restorationId: 'foo',
child: const BucketSpy(),
),
);
manager.doSerialization();
expect(state.bucket, isNull);
});
testWidgets('no bucket for descendants when scope and id are null', (WidgetTester tester) async {
await tester.pumpWidget(
const RestorationScope(
restorationId: null,
child: BucketSpy(),
),
);
final BucketSpyState state = tester.state(find.byType(BucketSpy));
expect(state.bucket, isNull);
});
testWidgets('moving scope moves its data', (WidgetTester tester) async {
final MockRestorationManager manager = MockRestorationManager();
final Map<String, dynamic> rawData = <String, dynamic>{};
final RestorationBucket root = RestorationBucket.root(manager: manager, rawData: rawData);
final Key scopeKey = GlobalKey();
await tester.pumpWidget(
UnmanagedRestorationScope(
bucket: root,
child: Row(
textDirection: TextDirection.ltr,
children: <Widget>[
RestorationScope(
restorationId: 'fixed',
child: RestorationScope(
key: scopeKey,
restorationId: 'moving-child',
child: const BucketSpy(),
),
),
],
),
),
);
manager.doSerialization();
final BucketSpyState state = tester.state(find.byType(BucketSpy));
expect(state.bucket!.restorationId, 'moving-child');
expect((((rawData[childrenMapKey] as Map<Object?, Object?>)['fixed']! as Map<String, dynamic>)[childrenMapKey] as Map<Object?, Object?>).containsKey('moving-child'), isTrue);
final RestorationBucket bucket = state.bucket!;
state.bucket!.write('value', 11);
manager.doSerialization();
// Move scope.
await tester.pumpWidget(
UnmanagedRestorationScope(
bucket: root,
child: Row(
textDirection: TextDirection.ltr,
children: <Widget>[
RestorationScope(
restorationId: 'fixed',
child: Container(),
),
RestorationScope(
key: scopeKey,
restorationId: 'moving-child',
child: const BucketSpy(),
),
],
),
),
);
manager.doSerialization();
expect(state.bucket!.restorationId, 'moving-child');
expect(state.bucket, same(bucket));
expect(state.bucket!.read<int>('value'), 11);
expect((rawData[childrenMapKey] as Map<Object?, Object?>)['fixed'], isEmpty);
expect((rawData[childrenMapKey] as Map<Object?, Object?>).containsKey('moving-child'), isTrue);
});
});
}
Map<String, dynamic> _createRawDataSet() {
return <String, dynamic>{
valuesMapKey: <String, dynamic>{
'value1' : 10,
'value2' : 'Hello',
},
childrenMapKey: <String, dynamic>{
'child1' : <String, dynamic>{
valuesMapKey : <String, dynamic>{
'foo': 22,
},
},
'child2' : <String, dynamic>{
valuesMapKey : <String, dynamic>{
'bar': 33,
},
},
},
};
}