Skip to content

Commit db0cb00

Browse files
committed
[keyboard_detection] Add regression integration tests
Expand the integration test suite for the keyboard detection controller from 2 to 20 cases, driving the input-panel event channel with simulated platform messages so the tests are deterministic and need no real keyboard: - Initial unknown state and unknown fallback for unrecognized events. - All keyboard states: visibling, visible, hiding, hidden. - stateAsBool mapping, including the transitional states with and without includeTransitionalState. - Keyboard metrics (width, height/size, position) updates on show with dimensions, reset on hide, and isSizeLoaded / ensureSizeLoaded. - Robustness against malformed events (non-map / missing or wrong-typed state). - Notifications via the state stream and the onChanged callback. - Registered callbacks: delivery, self-unregister on returning false, unregisterCallback and unregisterAllCallbacks. - No further state changes are reported after dispose. This is a Tizen-exclusive plugin with no upstream package. Test-only change, recorded under a NEXT changelog entry without a version bump. Validated on a Raspberry Pi 4 (Tizen) device: 20 passed, 0 failed.
1 parent 4795039 commit db0cb00

2 files changed

Lines changed: 319 additions & 19 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## NEXT
2+
3+
* Add 18 integration test cases.
4+
15
## 0.1.0
26

37
* Initial release.

packages/keyboard_detection/example/integration_test/keyboard_detection_tizen_test.dart

Lines changed: 315 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'dart:async';
6+
57
import 'package:flutter/services.dart';
68
import 'package:flutter_test/flutter_test.dart';
79
import 'package:integration_test/integration_test.dart';
@@ -13,7 +15,9 @@ void main() {
1315
const String channelName = 'tizen/internal/inputpanel';
1416
const StandardMethodCodec codec = StandardMethodCodec();
1517

16-
Future<void> emit(WidgetTester tester, Map<String, Object?> payload) async {
18+
// Injects an event into the input-panel [EventChannel] the controller
19+
// listens to, simulating a message coming from the flutter-tizen embedder.
20+
Future<void> emit(WidgetTester tester, Object? payload) async {
1721
final ByteData data = codec.encodeSuccessEnvelope(payload);
1822
await tester.binding.defaultBinaryMessenger.handlePlatformMessage(
1923
channelName,
@@ -22,25 +26,317 @@ void main() {
2226
);
2327
}
2428

25-
testWidgets('reports visible on show event', (WidgetTester tester) async {
26-
final KeyboardDetectionController controller =
27-
KeyboardDetectionController();
28-
await emit(tester, <String, Object?>{'state': 'show'});
29-
await tester.pump();
30-
expect(controller.state, KeyboardState.visible);
31-
expect(controller.stateAsBool(), isTrue);
32-
await controller.dispose();
29+
group('state reporting', () {
30+
testWidgets('starts in the unknown state', (WidgetTester tester) async {
31+
final KeyboardDetectionController controller =
32+
KeyboardDetectionController();
33+
expect(controller.state, KeyboardState.unknown);
34+
expect(controller.stateAsBool(), isNull);
35+
await controller.dispose();
36+
});
37+
38+
testWidgets('reports visibling on will_show event',
39+
(WidgetTester tester) async {
40+
final KeyboardDetectionController controller =
41+
KeyboardDetectionController();
42+
await emit(tester, <String, Object?>{'state': 'will_show'});
43+
await tester.pump();
44+
expect(controller.state, KeyboardState.visibling);
45+
await controller.dispose();
46+
});
47+
48+
testWidgets('reports visible on show event', (WidgetTester tester) async {
49+
final KeyboardDetectionController controller =
50+
KeyboardDetectionController();
51+
await emit(tester, <String, Object?>{'state': 'show'});
52+
await tester.pump();
53+
expect(controller.state, KeyboardState.visible);
54+
expect(controller.stateAsBool(), isTrue);
55+
await controller.dispose();
56+
});
57+
58+
testWidgets('reports hiding on will_hide event',
59+
(WidgetTester tester) async {
60+
final KeyboardDetectionController controller =
61+
KeyboardDetectionController();
62+
await emit(tester, <String, Object?>{'state': 'show'});
63+
await tester.pump();
64+
await emit(tester, <String, Object?>{'state': 'will_hide'});
65+
await tester.pump();
66+
expect(controller.state, KeyboardState.hiding);
67+
await controller.dispose();
68+
});
69+
70+
testWidgets('reports hidden on hide event', (WidgetTester tester) async {
71+
final KeyboardDetectionController controller =
72+
KeyboardDetectionController();
73+
await emit(tester, <String, Object?>{'state': 'show'});
74+
await tester.pump();
75+
await emit(tester, <String, Object?>{'state': 'hide'});
76+
await tester.pump();
77+
expect(controller.state, KeyboardState.hidden);
78+
expect(controller.stateAsBool(), isFalse);
79+
await controller.dispose();
80+
});
81+
82+
testWidgets('falls back to unknown for an unrecognized event',
83+
(WidgetTester tester) async {
84+
final KeyboardDetectionController controller =
85+
KeyboardDetectionController();
86+
await emit(tester, <String, Object?>{'state': 'show'});
87+
await tester.pump();
88+
await emit(tester, <String, Object?>{'state': 'something_else'});
89+
await tester.pump();
90+
expect(controller.state, KeyboardState.unknown);
91+
await controller.dispose();
92+
});
93+
94+
testWidgets('ignores events without a valid state field',
95+
(WidgetTester tester) async {
96+
final KeyboardDetectionController controller =
97+
KeyboardDetectionController();
98+
// Reach a known state first, so the assertions verify that the invalid
99+
// events are ignored (state preserved) rather than merely matching the
100+
// initial unknown state.
101+
await emit(tester, <String, Object?>{'state': 'show'});
102+
await tester.pump();
103+
expect(controller.state, KeyboardState.visible);
104+
await emit(tester, <String, Object?>{'noState': true});
105+
await tester.pump();
106+
expect(controller.state, KeyboardState.visible);
107+
await emit(tester, <String, Object?>{'state': 123});
108+
await tester.pump();
109+
expect(controller.state, KeyboardState.visible);
110+
await controller.dispose();
111+
});
112+
});
113+
114+
group('stateAsBool', () {
115+
testWidgets('maps the transitional visibling state',
116+
(WidgetTester tester) async {
117+
final KeyboardDetectionController controller =
118+
KeyboardDetectionController();
119+
await emit(tester, <String, Object?>{'state': 'will_show'});
120+
await tester.pump();
121+
expect(controller.stateAsBool(), isFalse);
122+
expect(controller.stateAsBool(true), isTrue);
123+
await controller.dispose();
124+
});
125+
126+
testWidgets('maps the transitional hiding state',
127+
(WidgetTester tester) async {
128+
final KeyboardDetectionController controller =
129+
KeyboardDetectionController();
130+
await emit(tester, <String, Object?>{'state': 'will_hide'});
131+
await tester.pump();
132+
expect(controller.stateAsBool(), isTrue);
133+
expect(controller.stateAsBool(true), isFalse);
134+
await controller.dispose();
135+
});
136+
});
137+
138+
group('keyboard metrics', () {
139+
testWidgets('size, width and position are zero before any event',
140+
(WidgetTester tester) async {
141+
final KeyboardDetectionController controller =
142+
KeyboardDetectionController();
143+
expect(controller.size, 0);
144+
expect(controller.width, 0);
145+
expect(controller.position, Offset.zero);
146+
expect(controller.isSizeLoaded, isFalse);
147+
await controller.dispose();
148+
});
149+
150+
testWidgets('updates metrics from a show event carrying dimensions',
151+
(WidgetTester tester) async {
152+
final KeyboardDetectionController controller =
153+
KeyboardDetectionController();
154+
await emit(tester, <String, Object?>{
155+
'state': 'show',
156+
'width': 1080.0,
157+
'height': 420.0,
158+
'x': 0.0,
159+
'y': 1500.0,
160+
});
161+
await tester.pump();
162+
expect(controller.width, 1080.0);
163+
expect(controller.size, 420.0);
164+
expect(controller.position, const Offset(0, 1500));
165+
expect(controller.isSizeLoaded, isTrue);
166+
await controller.dispose();
167+
});
168+
169+
testWidgets('ensureSizeLoaded completes once metrics arrive',
170+
(WidgetTester tester) async {
171+
final KeyboardDetectionController controller =
172+
KeyboardDetectionController();
173+
final Future<void> sizeLoaded = controller.ensureSizeLoaded;
174+
await emit(tester, <String, Object?>{
175+
'state': 'show',
176+
'width': 1080.0,
177+
'height': 420.0,
178+
});
179+
await tester.pump();
180+
await expectLater(sizeLoaded, completes);
181+
expect(controller.isSizeLoaded, isTrue);
182+
await controller.dispose();
183+
});
184+
185+
testWidgets('resets metrics to zero on hide', (WidgetTester tester) async {
186+
final KeyboardDetectionController controller =
187+
KeyboardDetectionController();
188+
await emit(tester, <String, Object?>{
189+
'state': 'show',
190+
'width': 1080.0,
191+
'height': 420.0,
192+
'x': 0.0,
193+
'y': 1500.0,
194+
});
195+
await tester.pump();
196+
await emit(tester, <String, Object?>{'state': 'hide'});
197+
await tester.pump();
198+
expect(controller.width, 0);
199+
expect(controller.size, 0);
200+
expect(controller.position, Offset.zero);
201+
await controller.dispose();
202+
});
203+
});
204+
205+
group('notifications', () {
206+
testWidgets('stream emits state changes in order',
207+
(WidgetTester tester) async {
208+
final KeyboardDetectionController controller =
209+
KeyboardDetectionController();
210+
final List<KeyboardState> seen = <KeyboardState>[];
211+
final StreamSubscription<KeyboardState> subscription =
212+
controller.stream.listen(seen.add);
213+
await emit(tester, <String, Object?>{'state': 'will_show'});
214+
await tester.pump();
215+
await emit(tester, <String, Object?>{'state': 'show'});
216+
await tester.pump();
217+
await emit(tester, <String, Object?>{'state': 'hide'});
218+
await tester.pump();
219+
expect(seen, <KeyboardState>[
220+
KeyboardState.visibling,
221+
KeyboardState.visible,
222+
KeyboardState.hidden,
223+
]);
224+
await subscription.cancel();
225+
await controller.dispose();
226+
});
227+
228+
testWidgets('onChanged is invoked on every state change',
229+
(WidgetTester tester) async {
230+
final List<KeyboardState> seen = <KeyboardState>[];
231+
final KeyboardDetectionController controller =
232+
KeyboardDetectionController(onChanged: seen.add);
233+
await emit(tester, <String, Object?>{'state': 'show'});
234+
await tester.pump();
235+
await emit(tester, <String, Object?>{'state': 'hide'});
236+
await tester.pump();
237+
expect(seen, <KeyboardState>[
238+
KeyboardState.visible,
239+
KeyboardState.hidden,
240+
]);
241+
await controller.dispose();
242+
});
243+
});
244+
245+
group('registered callbacks', () {
246+
testWidgets('a registered callback receives state changes',
247+
(WidgetTester tester) async {
248+
final KeyboardDetectionController controller =
249+
KeyboardDetectionController();
250+
final List<KeyboardState> seen = <KeyboardState>[];
251+
controller.registerCallback((KeyboardState state) {
252+
seen.add(state);
253+
return true;
254+
});
255+
await emit(tester, <String, Object?>{'state': 'show'});
256+
await tester.pump();
257+
await emit(tester, <String, Object?>{'state': 'hide'});
258+
await tester.pump();
259+
expect(seen, <KeyboardState>[
260+
KeyboardState.visible,
261+
KeyboardState.hidden,
262+
]);
263+
await controller.dispose();
264+
});
265+
266+
testWidgets('a callback returning false unregisters itself',
267+
(WidgetTester tester) async {
268+
final KeyboardDetectionController controller =
269+
KeyboardDetectionController();
270+
int calls = 0;
271+
controller.registerCallback((KeyboardState state) {
272+
calls++;
273+
return false;
274+
});
275+
await emit(tester, <String, Object?>{'state': 'show'});
276+
await tester.pump();
277+
await emit(tester, <String, Object?>{'state': 'hide'});
278+
await tester.pump();
279+
expect(calls, 1);
280+
await controller.dispose();
281+
});
282+
283+
testWidgets('unregisterCallback stops further invocations',
284+
(WidgetTester tester) async {
285+
final KeyboardDetectionController controller =
286+
KeyboardDetectionController();
287+
final List<KeyboardState> seen = <KeyboardState>[];
288+
bool callback(KeyboardState state) {
289+
seen.add(state);
290+
return true;
291+
}
292+
293+
controller.registerCallback(callback);
294+
await emit(tester, <String, Object?>{'state': 'show'});
295+
await tester.pump();
296+
controller.unregisterCallback(callback);
297+
await emit(tester, <String, Object?>{'state': 'hide'});
298+
await tester.pump();
299+
expect(seen, <KeyboardState>[KeyboardState.visible]);
300+
await controller.dispose();
301+
});
302+
303+
testWidgets('unregisterAllCallbacks removes every callback',
304+
(WidgetTester tester) async {
305+
final KeyboardDetectionController controller =
306+
KeyboardDetectionController();
307+
int first = 0;
308+
int second = 0;
309+
controller.registerCallback((KeyboardState state) {
310+
first++;
311+
return true;
312+
});
313+
controller.registerCallback((KeyboardState state) {
314+
second++;
315+
return true;
316+
});
317+
await emit(tester, <String, Object?>{'state': 'show'});
318+
await tester.pump();
319+
controller.unregisterAllCallbacks();
320+
await emit(tester, <String, Object?>{'state': 'hide'});
321+
await tester.pump();
322+
expect(first, 1);
323+
expect(second, 1);
324+
await controller.dispose();
325+
});
33326
});
34327

35-
testWidgets('reports hidden on hide event', (WidgetTester tester) async {
36-
final KeyboardDetectionController controller =
37-
KeyboardDetectionController();
38-
await emit(tester, <String, Object?>{'state': 'show'});
39-
await tester.pump();
40-
await emit(tester, <String, Object?>{'state': 'hide'});
41-
await tester.pump();
42-
expect(controller.state, KeyboardState.hidden);
43-
expect(controller.stateAsBool(), isFalse);
44-
await controller.dispose();
328+
group('lifecycle', () {
329+
testWidgets('does not report state changes after dispose',
330+
(WidgetTester tester) async {
331+
final KeyboardDetectionController controller =
332+
KeyboardDetectionController();
333+
await emit(tester, <String, Object?>{'state': 'show'});
334+
await tester.pump();
335+
expect(controller.state, KeyboardState.visible);
336+
await controller.dispose();
337+
await emit(tester, <String, Object?>{'state': 'hide'});
338+
await tester.pump();
339+
expect(controller.state, KeyboardState.visible);
340+
});
45341
});
46342
}

0 commit comments

Comments
 (0)