blob: 609b6e28da1f4bb9e6f3d77ca9997f4275f84508 [file] [log] [blame]
// Copyright 2013 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.
// This file is hand-formatted.
// ignore_for_file: avoid_dynamic_calls
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:rfw/formats.dart' show parseDataFile, parseLibraryFile;
import 'package:rfw/rfw.dart';
void main() {
testWidgets('list lookup', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['core']), createCoreWidgets());
final DynamicContent data = DynamicContent(<String, Object?>{
'list': <Object?>[ 0, 1, 2, 3, 4 ],
});
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
),
);
expect(find.byType(RemoteWidget), findsOneWidget);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
expect(find.byType(ErrorWidget), findsOneWidget);
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root = Column(
children: [
...for v in data.list: Text(text: v, textDirection: "ltr"),
],
);
'''));
await tester.pump();
expect(find.byType(Text), findsNWidgets(5));
});
testWidgets('data updates', (WidgetTester tester) async {
int buildCount = 0;
int? lastValue;
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['core']), LocalWidgetLibrary(<String, LocalWidgetBuilder>{
'Test': (BuildContext context, DataSource source) {
buildCount += 1;
lastValue = source.v<int>(<Object>['value']);
return const SizedBox.shrink();
},
}));
final DynamicContent data = DynamicContent(<String, Object?>{
'list': <Object?>[
<String, Object?>{ 'a': 0 },
<String, Object?>{ 'a': 1 },
<String, Object?>{ 'a': 2 },
],
});
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
),
);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
expect(buildCount, 0);
expect(lastValue, isNull);
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root = Test(value: data.list.1.a);
'''));
await tester.pump();
expect(buildCount, 1);
expect(lastValue, 1);
data.update('list', <Object?>[
<String, Object?>{ 'a': 0 },
<String, Object?>{ 'a': 3 },
<String, Object?>{ 'a': 2 },
]);
await tester.pump();
expect(buildCount, 2);
expect(lastValue, 3);
data.update('list', <Object?>[
<String, Object?>{ 'a': 1 },
<String, Object?>{ 'a': 3 },
]);
await tester.pump();
expect(buildCount, 2);
expect(lastValue, 3);
data.update('list', <Object?>[
<String, Object?>{ 'a': 1 },
<String, Object?>{ },
]);
await tester.pump();
expect(buildCount, 3);
expect(lastValue, null);
data.update('list', <Object?>[
<String, Object?>{ 'a': 1 },
]);
await tester.pump();
expect(buildCount, 3);
expect(lastValue, null);
});
testWidgets('$RemoteFlutterWidgetsException', (WidgetTester tester) async {
expect(const RemoteFlutterWidgetsException('test').toString(), 'test');
});
testWidgets('deepClone', (WidgetTester tester) async {
final Map<String, Object> map = <String, Object>{
'outer': <String, Object>{
'inner': true,
}
};
expect(identical(deepClone(map), map), isFalse);
expect(deepClone(map), equals(map));
});
testWidgets('updateText, updateBinary, clearLibraries', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['core']), createCoreWidgets());
final DynamicContent data = DynamicContent();
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
),
);
expect(find.byType(RemoteWidget), findsOneWidget);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
expect(find.byType(ErrorWidget), findsOneWidget);
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root = ColoredBox(color: 0xFF000000);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF000000));
runtime.update(const LibraryName(<String>['test']), decodeLibraryBlob(encodeLibraryBlob(parseLibraryFile('''
import core;
widget root = ColoredBox(color: 0xFF000001);
'''))));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF000001));
runtime.clearLibraries();
await tester.pump();
expect(find.byType(RemoteWidget), findsOneWidget);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
expect(find.byType(ErrorWidget), findsOneWidget);
});
testWidgets('Runtime cached build', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['core']), createCoreWidgets());
final DynamicContent data = DynamicContent();
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['core']), 'Placeholder'),
),
);
expect(find.byType(Placeholder), findsOneWidget);
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['core']), 'SizedBoxShrink'),
),
);
expect(find.byType(Placeholder), findsNothing);
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['core']), 'Placeholder'),
),
);
expect(find.byType(Placeholder), findsOneWidget);
});
testWidgets('Import loops', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['a']), parseLibraryFile('''
import b;
'''))
..update(const LibraryName(<String>['b']), parseLibraryFile('''
import a;
'''));
final DynamicContent data = DynamicContent();
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['a']), 'widget'),
),
);
expect(tester.takeException().toString(), 'Library a indirectly depends on itself via b which depends on a.');
});
testWidgets('Import loops', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['a']), parseLibraryFile('''
import a;
'''));
final DynamicContent data = DynamicContent();
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['a']), 'widget'),
),
);
expect(tester.takeException().toString(), 'Library a depends on itself.');
});
testWidgets('Missing libraries in import', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['a']), parseLibraryFile('''
import b;
'''));
final DynamicContent data = DynamicContent();
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['a']), 'widget'),
),
);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
expect(tester.widget<ErrorWidget>(find.byType(ErrorWidget)).message, 'Could not find remote widget named widget in a, possibly because some dependencies were missing: b');
});
testWidgets('Missing libraries in specified widget', (WidgetTester tester) async {
final Runtime runtime = Runtime();
final DynamicContent data = DynamicContent();
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['a']), 'widget'),
),
);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
expect(tester.widget<ErrorWidget>(find.byType(ErrorWidget)).message, 'Could not find remote widget named widget in a, possibly because some dependencies were missing: a');
});
testWidgets('Missing libraries in import via dependency', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['a']), parseLibraryFile('''
import b;
widget widget = test();
'''));
final DynamicContent data = DynamicContent();
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['a']), 'widget'),
),
);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
expect(tester.widget<ErrorWidget>(find.byType(ErrorWidget)).message, 'Could not find remote widget named test in a, possibly because some dependencies were missing: b');
});
testWidgets('Missing widget', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['a']), parseLibraryFile(''));
final DynamicContent data = DynamicContent();
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['a']), 'widget'),
),
);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
expect(tester.widget<ErrorWidget>(find.byType(ErrorWidget)).message, 'Could not find remote widget named widget in a.');
});
testWidgets('Runtime', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['core']), createCoreWidgets());
final DynamicContent data = DynamicContent();
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
),
);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root { level: 0 } = inner(level: state.level);
widget inner { level: 1 } = ColoredBox(color: args.level);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000000));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root { level: 0 } = inner(level: state.level);
widget inner { level: 1 } = ColoredBox(color: state.level);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000001));
});
testWidgets('Runtime', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['core']), createCoreWidgets());
final DynamicContent data = DynamicContent();
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
),
);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root { level: 0 } = switch state.level {
0: ColoredBox(color: 2),
};
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000002));
});
testWidgets('Runtime', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['core']), createCoreWidgets());
final DynamicContent data = DynamicContent();
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
),
);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root { level: 0 } = GestureDetector(
onTap: set state.level = 1,
child: ColoredBox(color: state.level),
);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000000));
await tester.tap(find.byType(ColoredBox));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000001));
});
testWidgets('DynamicContent', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['core']), createCoreWidgets());
final DynamicContent data = DynamicContent();
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
),
);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root = ColoredBox(color: data.color.value);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF000000));
data.update('color', json.decode('{"value":1}'));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000001));
data.update('color', parseDataFile('{value:2}'));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000002));
data.update('color', decodeDataBlob(Uint8List.fromList(<int>[
0xFE, 0x52, 0x57, 0x44, // signature
0x07, // data is a map
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ...which has one key
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ...which has five letters
0x76, 0x61, 0x6c, 0x75, 0x65, // ...which are "value"
0x02, // and the value is an integer
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ...which is the number 2
])));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000002));
});
testWidgets('DynamicContent', (WidgetTester tester) async {
final DynamicContent data = DynamicContent(<String, Object?>{'hello': 'world'});
expect(data.toString(), 'DynamicContent({hello: world})');
});
testWidgets('binding loop variables', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['core']), createCoreWidgets());
final DynamicContent data = DynamicContent(<String, Object?>{
'list': <Object?>[
<String, Object?>{
'a': <String, Object?>{ 'b': 0xEE },
'c': <Object?>[ 0xDD ],
},
],
});
final List<String> eventLog = <String>[];
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
onEvent: (String eventName, DynamicMap eventArguments) {
eventLog.add('$eventName $eventArguments');
},
),
);
expect(find.byType(RemoteWidget), findsOneWidget);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
expect(find.byType(ErrorWidget), findsOneWidget);
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget verify = ColoredBox(color: args.value.0.q.0);
widget root = verify(
value: [
...for v in data.list: {
q: [ v.a.b ],
},
],
);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x000000EE));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget verify = ColoredBox(color: args.value.0.q.0);
widget root = verify(
value: [
...for v in data.list: {
q: [ ...for w in v.c: w ],
},
],
);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x000000DD));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget verify = ColoredBox(color: args.value.0.q);
widget root = verify(
value: [
...for v in data.list: {
q: switch v.a.b {
0xEE: 0xCC,
default: 0xFF,
},
},
],
);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x000000CC));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget verify { state: true } = ColoredBox(color: args.value.c.0);
widget remote = SizedBox(child: args.corn.0);
widget root = remote(
corn: [
...for v in data.list:
verify(value: v),
],
);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x000000DD));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget verify { state: true } = ColoredBox(color: args.value);
widget remote = SizedBox(child: args.corn.0);
widget root = remote(
corn: [
...for v in data.list:
verify(value: switch v.c.0 {
0: 0xFF000000,
0xDD: 0xFF0D0D0D,
default: v,
}),
],
);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF0D0D0D));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget verify { state: true } = switch args.value.c.0 {
0xDD: ColoredBox(color: 0xFF0D0D0D),
default: ColoredBox(color: args.value),
};
widget remote = SizedBox(child: args.corn.0);
widget root = remote(
corn: [
...for v in data.list:
verify(value: v),
],
);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF0D0D0D));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget verify { state: true } = GestureDetector(
onTap: event 'test' { test: args.value.a.b },
child: ColoredBox(),
);
widget remote = SizedBox(child: args.corn.0);
widget root = remote(
corn: [
...for v in data.list:
verify(value: v),
],
);
'''));
expect(eventLog, isEmpty);
await tester.pump();
await tester.tap(find.byType(ColoredBox));
expect(eventLog, <String>['test {test: ${0xEE}}']);
eventLog.clear();
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget verify { state: 0x00 } = GestureDetector(
onTap: set state.state = args.value.a.b,
child: ColoredBox(color: switch state.state {
0x00: 0xFF000001,
0xEE: 0xFF000002,
}),
);
widget remote = SizedBox(child: args.corn.0);
widget root = remote(
corn: [
...for v in data.list:
verify(value: v),
],
);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF000001));
await tester.tap(find.byType(ColoredBox));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF000002));
});
testWidgets('list lookup of esoteric values', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['core']), createCoreWidgets());
final DynamicContent data = DynamicContent();
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
),
);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root = test(list: ['A'], loop: [...for b in ["B", "C"]: b]);
widget test = Text(
textDirection: "ltr",
text: [
'>',
...for a in args.list: a,
...for b in args.loop: b,
'<',
],
);
'''));
await tester.pump();
expect(find.text('>ABC<'), findsOneWidget);
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root = Text(
textDirection: "ltr",
text: [
'>',
...for a in root(): a,
'<',
],
);
'''));
await tester.pump();
expect(find.text('><'), findsOneWidget);
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root = test(list: [test()]);
widget test = Text(
textDirection: "ltr",
text: [
'>',
...for a in args.list: a,
'<',
],
);
'''));
await tester.pump();
expect(find.text('><'), findsOneWidget);
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root { list: [ 0x01 ] } = GestureDetector(
onTap: set state.list = [ 0x02, 0x03 ],
child: Column(
children: [
...for v in state.list:
SizedBox(height: 10.0, width: 10.0, child: ColoredBox(color: v)),
],
),
);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000001));
await tester.tap(find.byType(ColoredBox));
await tester.pump();
expect(tester.firstWidget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000002));
expect(find.byType(ColoredBox), findsNWidgets(2));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root = Column(
children: [
...for v in switch 0 {
0: [ColoredBox(color: 0x00000001)],
default: [ColoredBox(color: 0xFFFF0000)],
}: v,
],
);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000001));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root = Column(
children: [
...for v in {
a: [ColoredBox(color: 0x00000001)],
b: [ColoredBox(color: 0xFFFF0000)],
}: v,
],
);
'''));
await tester.pump();
expect(find.byType(ColoredBox), findsNothing);
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root = test(
list: [...for w in [ColoredBox(color: 0xFF00FF00)]: w],
);
widget test = Column(
children: [
...for v in args.list: v,
],
);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF00FF00));
});
testWidgets('data lookup', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['core']), createCoreWidgets());
final DynamicContent data = DynamicContent(<String, Object?>{
'map': <String, Object?>{ 'list': <Object?>[ 0xAB ] },
});
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
),
);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root = test(list: data.map.list);
widget test = ColoredBox(color: args.list.0);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x000000AB));
});
testWidgets('args lookup', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['core']), createCoreWidgets());
final DynamicContent data = DynamicContent();
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
),
);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root = test1(map: { 'list': [ 0xAC ] });
widget test1 = test2(list: args.map.list);
widget test2 = ColoredBox(color: args.list.0);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x000000AC));
});
testWidgets('state lookup', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['core']), createCoreWidgets());
final DynamicContent data = DynamicContent();
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
),
);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root { map: { 'list': [ 0xAD ] } } = test(list: state.map.list);
widget test = ColoredBox(color: args.list.0);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x000000AD));
});
testWidgets('switch', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['core']), createCoreWidgets());
final DynamicContent data = DynamicContent();
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
),
);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root = ColoredBox(color: switch data.a.b {
0: 0x11111111,
default: 0x22222222,
});
'''));
data.update('a', parseDataFile('{ b: 1 }'));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x22222222));
data.update('a', parseDataFile('{ b: 0 }'));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x11111111));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root = switch true {};
'''));
await tester.pump();
expect(tester.takeException().toString(), 'Switch in test:root did not resolve to a widget (got <missing>).');
});
testWidgets('events with arguments', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['core']), createCoreWidgets());
final DynamicContent data = DynamicContent();
final List<String> eventLog = <String>[];
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
onEvent: (String eventName, DynamicMap eventArguments) {
eventLog.add('$eventName $eventArguments');
},
),
);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root = GestureDetector(
onTap: event 'tap' {
list: [...for a in [0,1]: a],
},
child: ColoredBox(),
);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF000000));
expect(eventLog, isEmpty);
await tester.tap(find.byType(ColoredBox));
expect(eventLog, <String>['tap {list: [0, 1]}']);
eventLog.clear();
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root = GestureDetector(
onTap: [ event 'tap' { a: 1 }, event 'tap' { a: 2 }, event 'final tap' { } ],
child: ColoredBox(),
);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF000000));
expect(eventLog, isEmpty);
await tester.tap(find.byType(ColoredBox));
expect(eventLog, <String>['tap {a: 1}', 'tap {a: 2}', 'final tap {}']);
eventLog.clear();
});
testWidgets('_CurriedWidget toStrings', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['core']), createCoreWidgets());
final DynamicContent data = DynamicContent();
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget stateless = ColoredBox(color: 0xAA);
widget stateful { test: false } = ColoredBox(color: 0xBB);
widget switchy = switch true { default: ColoredBox(color: 0xCC) };
'''));
expect(
(runtime.build(
tester.element(find.byType(Container)),
const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'stateless'),
data,
(String eventName, DynamicMap eventArguments) {},
) as dynamic).curriedWidget.toString(),
'core:ColoredBox {} {color: 170}',
);
expect(
(runtime.build(
tester.element(find.byType(Container)),
const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'stateful'),
data,
(String eventName, DynamicMap eventArguments) {},
) as dynamic).curriedWidget.toString(),
'test:stateful {test: false} {} = core:ColoredBox {} {color: 187}',
);
expect(
(runtime.build(
tester.element(find.byType(Container)),
const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'switchy'),
data,
(String eventName, DynamicMap eventArguments) {},
) as dynamic).curriedWidget.toString(),
'test:switchy {} {} = switch true {null: core:ColoredBox {} {color: 204}}',
);
});
testWidgets('state setting', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['core']), createCoreWidgets());
final DynamicContent data = DynamicContent();
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['test']), 'root'),
),
);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root { a: 0 } = GestureDetector(
onTap: set state.b = 0,
child: ColoredBox(),
);
'''));
await tester.pump();
await tester.tap(find.byType(ColoredBox));
expect(tester.takeException().toString(), 'b does not identify existing state.');
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root { a: 0 } = GestureDetector(
onTap: set state.0 = 0,
child: ColoredBox(),
);
'''));
await tester.pump();
await tester.tap(find.byType(ColoredBox));
expect(tester.takeException().toString(), '0 does not identify existing state.');
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root { a: [] } = GestureDetector(
onTap: set state.a.b = 0,
child: ColoredBox(),
);
'''));
await tester.pump();
await tester.tap(find.byType(ColoredBox));
expect(tester.takeException().toString(), 'a.b does not identify existing state.');
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root { a: [] } = GestureDetector(
onTap: set state.a.0 = 0,
child: ColoredBox(),
);
'''));
await tester.pump();
await tester.tap(find.byType(ColoredBox));
expect(tester.takeException().toString(), 'a.0 does not identify existing state.');
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root { a: true } = GestureDetector(
onTap: set state.a.0 = 0,
child: ColoredBox(),
);
'''));
await tester.pump();
await tester.tap(find.byType(ColoredBox));
expect(tester.takeException().toString(), 'a.0 does not identify existing state.');
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root { a: true } = GestureDetector(
onTap: set state.a.b = 0,
child: ColoredBox(),
);
'''));
await tester.pump();
await tester.tap(find.byType(ColoredBox));
expect(tester.takeException().toString(), 'a.b does not identify existing state.');
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root { a: { b: 0 } } = GestureDetector(
onTap: set state.a = 15,
child: ColoredBox(color: state.a),
);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF000000));
await tester.tap(find.byType(ColoredBox));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x0000000F));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root { a: [ 0 ] } = GestureDetector(
onTap: set state.a = 10,
child: ColoredBox(color: state.a),
);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xFF000000));
await tester.tap(find.byType(ColoredBox));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x0000000A));
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root { a: [ [ 1 ] ] } = GestureDetector(
onTap: set state.a.0.0 = 11,
child: ColoredBox(color: state.a.0.0),
);
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x00000001));
await tester.tap(find.byType(ColoredBox));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0x0000000B));
});
testWidgets('DataSource', (WidgetTester tester) async {
final Runtime runtime = Runtime();
final DynamicContent data = DynamicContent();
final List<String> eventLog = <String>[];
await tester.pumpWidget(
RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(LibraryName(<String>['remote']), 'test'),
onEvent: (String name, DynamicMap arguments) {
eventLog.add('$name $arguments');
},
),
);
expect(tester.takeException().toString(), contains('Could not find remote widget named'));
runtime.update(const LibraryName(<String>['local']), LocalWidgetLibrary(<String, LocalWidgetBuilder>{
'Test': (BuildContext context, DataSource source) {
expect(source.isList(<Object>['a']), isFalse);
expect(source.isList(<Object>['b']), isTrue);
expect(source.length(<Object>['b']), 1);
expect(source.child(<Object>['missing']), isA<ErrorWidget>());
expect(tester.takeException().toString(), 'Not a widget at [missing] (got <missing>) for local:Test.');
expect(source.childList(<Object>['a']), <Matcher>[isA<ErrorWidget>()]);
expect(tester.takeException().toString(), 'Not a widget list at [a] (got 0) for local:Test.');
expect(source.childList(<Object>['b']), <Matcher>[isA<ErrorWidget>()]);
expect(tester.takeException().toString(), 'Not a widget at [b] (got 1) for local:Test.');
expect(eventLog, isEmpty);
source.voidHandler(<Object>['callback'], <String, Object?>{ 'extra': 4, 'b': 3 })!();
expect(eventLog, <String>['e {a: 1, b: 3, extra: 4}']);
return const ColoredBox(color: Color(0xAABBCCDD));
},
}));
runtime.update(const LibraryName(<String>['remote']), parseLibraryFile('''
import local;
widget test = Test(a: 0, b: [1], callback: event 'e' { a: 1, b: 2 });
'''));
await tester.pump();
expect(tester.widget<ColoredBox>(find.byType(ColoredBox)).color, const Color(0xAABBCCDD));
bool tested = false;
tester.element(find.byType(ColoredBox)).visitAncestorElements((Element node) {
expect(node.toString(), equalsIgnoringHashCodes('_Widget(state: _WidgetState#00000(name: "local:Test"))'));
tested = true;
return false;
});
expect(tested, isTrue);
});
}