blob: bc45ad6e7cd910aacabe52fed342ebfd3940c8ae [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.
// This file is run as part of a reduced test set in CI on Mac and Windows
// machines.
@Tags(<String>['reduced-test-set'])
library;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/semantics.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('RawRadio control test', (WidgetTester tester) async {
final node = FocusNode();
addTearDown(node.dispose);
ToggleableStateMixin? actualState;
final registry = TestRegistry<int>();
Widget buildWidget() {
return RawRadio<int>(
value: 0,
mouseCursor: WidgetStateProperty.all<MouseCursor>(SystemMouseCursors.click),
toggleable: true,
focusNode: node,
autofocus: false,
enabled: true,
groupRegistry: registry,
builder: (BuildContext context, ToggleableStateMixin state) {
actualState = state;
return CustomPaint(size: const Size(40, 40), painter: TestPainter());
},
);
}
await tester.pumpWidget(buildWidget());
expect(actualState!.tristate, isTrue);
expect(actualState!.value, isFalse);
expect(registry.groupValue, isNull);
final State state = tester.state(find.byType(RawRadio<int>));
expect(registry.clients.contains(state as RadioClient<int>), isTrue);
await tester.tap(find.byType(RawRadio<int>));
// Rebuilds with new group value
await tester.pumpWidget(buildWidget());
expect(registry.groupValue, 0);
expect(actualState!.value, isTrue);
});
testWidgets('RawRadio disabled', (WidgetTester tester) async {
final node = FocusNode();
addTearDown(node.dispose);
final registry = TestRegistry<int>();
Widget buildWidget() {
return RawRadio<int>(
value: 0,
mouseCursor: WidgetStateProperty.all<MouseCursor>(SystemMouseCursors.click),
toggleable: true,
focusNode: node,
autofocus: false,
enabled: false,
groupRegistry: registry,
builder: (BuildContext context, ToggleableStateMixin state) {
return CustomPaint(size: const Size(40, 40), painter: TestPainter());
},
);
}
await tester.pumpWidget(buildWidget());
await tester.tap(find.byType(RawRadio<int>));
// onChanged won't fire
expect(registry.groupValue, isNull);
});
testWidgets('RawRadio enabled without registry throws', (WidgetTester tester) async {
final node = FocusNode();
addTearDown(node.dispose);
Widget buildWidget() {
return RawRadio<int>(
value: 0,
mouseCursor: WidgetStateProperty.all<MouseCursor>(SystemMouseCursors.click),
toggleable: true,
focusNode: node,
autofocus: false,
enabled: true,
groupRegistry: null,
builder: (BuildContext context, ToggleableStateMixin state) {
return CustomPaint(size: const Size(40, 40), painter: TestPainter());
},
);
}
await expectLater(() => tester.pumpWidget(buildWidget()), throwsAssertionError);
});
// Regression tests for https://github.com/flutter/flutter/issues/170422
group('Raw Radio accessibility announcements on various platforms', () {
testWidgets('Unselected radio should be vocalized via hint on iOS/macOS platform', (
WidgetTester tester,
) async {
final registry = TestRegistry<int>();
registry.groupValue = 2; // To mark radio as unselected.
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
final node = FocusNode();
addTearDown(node.dispose);
await tester.pumpWidget(
MaterialApp(
home: RawRadio<int>(
value: 1,
mouseCursor: WidgetStateProperty.all<MouseCursor>(SystemMouseCursors.click),
toggleable: false,
focusNode: node,
autofocus: false,
enabled: true,
groupRegistry: registry,
builder: (BuildContext context, ToggleableStateMixin state) {
return const SizedBox.square(dimension: 24);
},
),
),
);
final SemanticsNode semanticNode = tester.getSemantics(find.byType(RawRadio<int>));
// Radio semantics should have hint.
if (defaultTargetPlatform == TargetPlatform.iOS ||
defaultTargetPlatform == TargetPlatform.macOS) {
expect(semanticNode.hint, localizations.radioButtonUnselectedLabel);
} else {
expect(semanticNode.hint, anyOf(isNull, isEmpty));
}
});
testWidgets('Selected radio should be vocalized via the selected flag on all platforms', (
WidgetTester tester,
) async {
final registry = TestRegistry<int>();
registry.groupValue = 1; // To mark radio as selected.
final node = FocusNode();
addTearDown(node.dispose);
await tester.pumpWidget(
MaterialApp(
home: RawRadio<int>(
value: 1,
mouseCursor: WidgetStateProperty.all<MouseCursor>(SystemMouseCursors.click),
toggleable: false,
focusNode: node,
autofocus: false,
enabled: true,
groupRegistry: registry,
builder: (BuildContext context, ToggleableStateMixin state) {
return const SizedBox.square(dimension: 24);
},
),
),
);
final SemanticsNode semantics = tester.getSemantics(find.byType(RawRadio<int>));
// Radio semantics should not have hint.
expect(semantics.hint, anyOf(isNull, isEmpty));
});
});
}
class TestRegistry<T> extends RadioGroupRegistry<T> {
final Set<RadioClient<T>> clients = <RadioClient<T>>{};
@override
T? groupValue;
@override
ValueChanged<T?> get onChanged =>
(T? newValue) => groupValue = newValue;
@override
void registerClient(RadioClient<T> radio) => clients.add(radio);
@override
void unregisterClient(RadioClient<T> radio) => clients.remove(radio);
}
class TestPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}