blob: c52988a754634949cdd044f5c3f23b1fb48caf26 [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 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
typedef InspectorServiceExtensionCallback = FutureOr<Map<String, Object?>> Function(Map<String, String> parameters);
class StructureErrorTestWidgetInspectorService extends Object with WidgetInspectorService {
final Map<String, InspectorServiceExtensionCallback> extensions = <String, InspectorServiceExtensionCallback>{};
final Map<String, List<Map<Object, Object?>>> eventsDispatched = <String, List<Map<Object, Object?>>>{};
void registerServiceExtension({
required String name,
required FutureOr<Map<String, Object?>> callback(Map<String, String> parameters),
}) {
extensions[name] = callback;
void postEvent(String eventKind, Map<Object, Object?> eventData) {
List<Map<Object, Object?>> getEventsDispatched(String eventKind) {
return eventsDispatched.putIfAbsent(eventKind, () => <Map<Object, Object?>>[]);
Iterable<Map<Object, Object?>> getServiceExtensionStateChangedEvents(String extensionName) {
return getEventsDispatched('Flutter.ServiceExtensionStateChanged')
.where((Map<Object, Object?> event) => event['extension'] == extensionName);
Future<String> testBoolExtension(String name, Map<String, String> arguments) async {
expect(extensions, contains(name));
// Encode and decode to JSON to match behavior using a real service
// extension where only JSON is allowed.
return json.decode(json.encode(await extensions[name]!(arguments)))['enabled'] as String;
static void runTests() {
final StructureErrorTestWidgetInspectorService service = StructureErrorTestWidgetInspectorService();
WidgetInspectorService.instance = service;
test('ext.flutter.inspector.structuredErrors reports error to _structuredExceptionHandler on error', () async {
final FlutterExceptionHandler? oldHandler = FlutterError.onError;
bool usingNewHandler = false;
// Creates a spy onError. This spy needs to be set before widgets binding
// initializes.
FlutterError.onError = (FlutterErrorDetails details) {
usingNewHandler = true;
try {
// Enables structured errors.
expect(await service.testBoolExtension(
'structuredErrors', <String, String>{'enabled': 'true'}),
// Creates an error.
final FlutterErrorDetails expectedError = FlutterErrorDetails(
library: 'rendering library',
context: ErrorDescription('during layout'),
exception: StackTrace.current,
// For non-web apps, this validates the new handler did not receive an
// error because `FlutterError.onError` was set to
// `WidgetInspectorService._structuredExceptionHandler` when service
// extensions were initialized. For web apps, the new handler should
// have received an error because structured errors are disabled by
// default on the web.
expect(usingNewHandler, equals(kIsWeb));
} finally {
FlutterError.onError = oldHandler;