@@ -2,22 +2,26 @@ import 'dart:convert';
22
33import 'package:checks/checks.dart' ;
44import 'package:flutter/material.dart' ;
5+ import 'package:flutter/services.dart' ;
56import 'package:flutter_test/flutter_test.dart' ;
67import 'package:http/http.dart' as http;
78import 'package:zulip/api/model/initial_snapshot.dart' ;
89import 'package:zulip/api/model/model.dart' ;
910import 'package:zulip/api/model/narrow.dart' ;
1011import 'package:zulip/api/route/messages.dart' ;
12+ import 'package:zulip/model/binding.dart' ;
1113import 'package:zulip/model/localizations.dart' ;
1214import 'package:zulip/model/narrow.dart' ;
1315import 'package:zulip/model/store.dart' ;
1416import 'package:zulip/widgets/actions.dart' ;
1517
1618import '../api/fake_api.dart' ;
1719import '../example_data.dart' as eg;
20+ import '../flutter_checks.dart' ;
1821import '../model/binding.dart' ;
1922import '../model/unreads_checks.dart' ;
2023import '../stdlib_checks.dart' ;
24+ import '../test_clipboard.dart' ;
2125import 'dialog_checks.dart' ;
2226import 'test_app.dart' ;
2327
@@ -340,4 +344,75 @@ void main() {
340344 });
341345 });
342346 });
347+
348+ group ('PlatformActions' , () {
349+ TestZulipBinding .ensureInitialized ();
350+ TestWidgetsFlutterBinding .ensureInitialized ();
351+
352+ setUp (() async {
353+ TestDefaultBinaryMessengerBinding .instance.defaultBinaryMessenger.setMockMethodCallHandler (
354+ SystemChannels .platform,
355+ MockClipboard ().handleMethodCall,
356+ );
357+ });
358+
359+ tearDown (() async {
360+ testBinding.reset ();
361+ });
362+
363+ group ('copyWithPopup' , () {
364+ Future <void > call (WidgetTester tester, {required String text}) async {
365+ await tester.pumpWidget (TestZulipApp (
366+ child: Scaffold (
367+ body: Builder (builder: (context) => Center (
368+ child: ElevatedButton (
369+ onPressed: () async {
370+ PlatformActions .copyWithPopup (context: context,
371+ successContent: const Text ('Text copied' ),
372+ data: ClipboardData (text: text));
373+ },
374+ child: const Text ('Copy' )))))));
375+ await tester.pump ();
376+ await tester.tap (find.text ('Copy' ));
377+ await tester.pump (); // copy
378+ await tester.pump (Duration .zero); // await platform info (awkwardly async)
379+ }
380+
381+ Future <void > checkSnackBar (WidgetTester tester, {required bool expected}) async {
382+ if (! expected) {
383+ check (tester.widgetList (find.byType (SnackBar ))).isEmpty ();
384+ return ;
385+ }
386+ final snackBar = tester.widget <SnackBar >(find.byType (SnackBar ));
387+ check (snackBar.behavior).equals (SnackBarBehavior .floating);
388+ tester.widget (find.descendant (matchRoot: true ,
389+ of: find.byWidget (snackBar.content), matching: find.text ('Text copied' )));
390+ }
391+
392+ Future <void > checkClipboardText (String expected) async {
393+ check (await Clipboard .getData ('text/plain' )).isNotNull ().text.equals (expected);
394+ }
395+
396+ testWidgets ('iOS' , (tester) async {
397+ testBinding.deviceInfoResult = const IosDeviceInfo (systemVersion: '16.0' );
398+ await call (tester, text: 'asdf' );
399+ await checkClipboardText ('asdf' );
400+ await checkSnackBar (tester, expected: true );
401+ });
402+
403+ testWidgets ('Android' , (tester) async {
404+ testBinding.deviceInfoResult = const AndroidDeviceInfo (sdkInt: 33 , release: '13' );
405+ await call (tester, text: 'asdf' );
406+ await checkClipboardText ('asdf' );
407+ await checkSnackBar (tester, expected: false );
408+ });
409+
410+ testWidgets ('Android <13' , (tester) async {
411+ testBinding.deviceInfoResult = const AndroidDeviceInfo (sdkInt: 32 , release: '12' );
412+ await call (tester, text: 'asdf' );
413+ await checkClipboardText ('asdf' );
414+ await checkSnackBar (tester, expected: true );
415+ });
416+ });
417+ });
343418}
0 commit comments