@@ -41,6 +41,8 @@ void main() {
41
41
42
42
Future <GlobalKey <ComposeBoxController >> prepareComposeBox (WidgetTester tester, {
43
43
required Narrow narrow,
44
+ User ? selfUser,
45
+ int ? realmWaitingPeriodThreshold,
44
46
List <User > users = const [],
45
47
List <ZulipStream > streams = const [],
46
48
}) async {
@@ -49,16 +51,19 @@ void main() {
49
51
'Add a channel with "streamId" the same as of $narrow .streamId to the store.' );
50
52
}
51
53
addTearDown (testBinding.reset);
52
- await testBinding.globalStore.add (eg.selfAccount, eg.initialSnapshot ());
54
+ selfUser ?? = eg.selfUser;
55
+ final selfAccount = eg.account (user: selfUser);
56
+ await testBinding.globalStore.add (selfAccount, eg.initialSnapshot (
57
+ realmWaitingPeriodThreshold: realmWaitingPeriodThreshold));
53
58
54
- store = await testBinding.globalStore.perAccount (eg. selfAccount.id);
59
+ store = await testBinding.globalStore.perAccount (selfAccount.id);
55
60
56
- await store.addUsers ([eg. selfUser, ...users]);
61
+ await store.addUsers ([selfUser, ...users]);
57
62
await store.addStreams (streams);
58
63
connection = store.connection as FakeApiConnection ;
59
64
60
65
final controllerKey = GlobalKey <ComposeBoxController >();
61
- await tester.pumpWidget (TestZulipApp (accountId: eg. selfAccount.id,
66
+ await tester.pumpWidget (TestZulipApp (accountId: selfAccount.id,
62
67
child: Column (
63
68
// This positions the compose box at the bottom of the screen,
64
69
// simulating the layout of the message list page.
@@ -303,17 +308,20 @@ void main() {
303
308
304
309
Future <void > prepareComposeBoxWithNavigation (WidgetTester tester) async {
305
310
addTearDown (testBinding.reset);
306
- await testBinding.globalStore.add (eg.selfAccount, eg.initialSnapshot ());
311
+ final selfUser = eg.selfUser;
312
+ final selfAccount = eg.account (user: selfUser);
313
+ await testBinding.globalStore.add (selfAccount, eg.initialSnapshot ());
307
314
308
- store = await testBinding.globalStore.perAccount (eg.selfAccount.id);
315
+ store = await testBinding.globalStore.perAccount (selfAccount.id);
316
+ await store.addUser (selfUser);
309
317
await store.addStream (channel);
310
318
connection = store.connection as FakeApiConnection ;
311
319
312
320
await tester.pumpWidget (const ZulipApp ());
313
321
await tester.pump ();
314
322
final navigator = await ZulipApp .navigator;
315
323
unawaited (navigator.push (MaterialAccountWidgetRoute (
316
- accountId: eg. selfAccount.id, page: ComposeBox (narrow: narrow))));
324
+ accountId: selfAccount.id, page: ComposeBox (narrow: narrow))));
317
325
await tester.pumpAndSettle ();
318
326
}
319
327
@@ -581,7 +589,9 @@ void main() {
581
589
});
582
590
583
591
group ('error banner' , () {
584
- Finder contentFieldFinder () => find.descendant (
592
+ final zulipLocalizations = GlobalLocalizations .zulipLocalizations;
593
+
594
+ Finder inputFieldFinder () => find.descendant (
585
595
of: find.byType (ComposeBox ),
586
596
matching: find.byType (TextField ));
587
597
@@ -590,24 +600,26 @@ void main() {
590
600
matching: find.widgetWithIcon (IconButton , icon));
591
601
592
602
void checkComposeBoxParts ({required bool areShown}) {
593
- check (contentFieldFinder ().evaluate ().length).equals (areShown ? 1 : 0 );
603
+ final inputFieldCount = inputFieldFinder ().evaluate ().length;
604
+ areShown ? check (inputFieldCount).isGreaterThan (0 ) : check (inputFieldCount).equals (0 );
594
605
check (attachButtonFinder (Icons .attach_file).evaluate ().length).equals (areShown ? 1 : 0 );
595
606
check (attachButtonFinder (Icons .image).evaluate ().length).equals (areShown ? 1 : 0 );
596
607
check (attachButtonFinder (Icons .camera_alt).evaluate ().length).equals (areShown ? 1 : 0 );
597
608
}
598
609
599
- void checkBanner ({required bool isShown}) {
600
- final bannerTextFinder = find.text (GlobalLocalizations .zulipLocalizations
601
- .errorBannerDeactivatedDmLabel);
602
- check (bannerTextFinder.evaluate ().length).equals (isShown ? 1 : 0 );
610
+ void checkBannerWithLabel (String label, {required bool isShown}) {
611
+ check (find.text (label).evaluate ().length).equals (isShown ? 1 : 0 );
603
612
}
604
613
605
- void checkComposeBox ( {required bool isShown }) {
614
+ void checkComposeBoxIsShown ( bool isShown, {required String bannerLabel }) {
606
615
checkComposeBoxParts (areShown: isShown);
607
- checkBanner ( isShown: ! isShown);
616
+ checkBannerWithLabel (bannerLabel, isShown: ! isShown);
608
617
}
609
618
610
619
group ('in DMs with deactivated users' , () {
620
+ void checkComposeBox ({required bool isShown}) => checkComposeBoxIsShown (isShown,
621
+ bannerLabel: zulipLocalizations.errorBannerDeactivatedDmLabel);
622
+
611
623
Future <void > changeUserStatus (WidgetTester tester,
612
624
{required User user, required bool isActive}) async {
613
625
await store.handleEvent (RealmUserUpdateEvent (id: 1 ,
@@ -686,5 +698,101 @@ void main() {
686
698
});
687
699
});
688
700
});
701
+
702
+ group ('in channel/topic narrow according to channel post policy' , () {
703
+ void checkComposeBox ({required bool isShown}) => checkComposeBoxIsShown (isShown,
704
+ bannerLabel: zulipLocalizations.errorBannerCannotPostInChannelLabel);
705
+
706
+ final narrowTestCases = [
707
+ ('channel' , const ChannelNarrow (1 )),
708
+ ('topic' , const TopicNarrow (1 , 'topic' )),
709
+ ];
710
+
711
+ for (final (String narrowType, Narrow narrow) in narrowTestCases) {
712
+ testWidgets ('compose box is shown in $narrowType narrow' , (tester) async {
713
+ await prepareComposeBox (tester,
714
+ narrow: narrow,
715
+ selfUser: eg.user (role: UserRole .administrator),
716
+ streams: [eg.stream (streamId: 1 ,
717
+ channelPostPolicy: ChannelPostPolicy .moderators)]);
718
+ checkComposeBox (isShown: true );
719
+ });
720
+
721
+ testWidgets ('error banner is shown in $narrowType narrow' , (tester) async {
722
+ await prepareComposeBox (tester,
723
+ narrow: narrow,
724
+ selfUser: eg.user (role: UserRole .moderator),
725
+ streams: [eg.stream (streamId: 1 ,
726
+ channelPostPolicy: ChannelPostPolicy .administrators)]);
727
+ checkComposeBox (isShown: false );
728
+ });
729
+ }
730
+
731
+ testWidgets ('user loses privilege -> compose box is replaced with the banner' , (tester) async {
732
+ final selfUser = eg.user (role: UserRole .administrator);
733
+ await prepareComposeBox (tester,
734
+ narrow: const ChannelNarrow (1 ),
735
+ selfUser: selfUser,
736
+ streams: [eg.stream (streamId: 1 ,
737
+ channelPostPolicy: ChannelPostPolicy .administrators)]);
738
+ checkComposeBox (isShown: true );
739
+
740
+ await store.handleEvent (RealmUserUpdateEvent (id: 1 ,
741
+ userId: selfUser.userId, role: UserRole .moderator));
742
+ await tester.pump ();
743
+ checkComposeBox (isShown: false );
744
+ });
745
+
746
+ testWidgets ('user gains privilege -> banner is replaced with the compose box' , (tester) async {
747
+ final selfUser = eg.user (role: UserRole .guest);
748
+ await prepareComposeBox (tester,
749
+ narrow: const ChannelNarrow (1 ),
750
+ selfUser: selfUser,
751
+ streams: [eg.stream (streamId: 1 ,
752
+ channelPostPolicy: ChannelPostPolicy .moderators)]);
753
+ checkComposeBox (isShown: false );
754
+
755
+ await store.handleEvent (RealmUserUpdateEvent (id: 1 ,
756
+ userId: selfUser.userId, role: UserRole .administrator));
757
+ await tester.pump ();
758
+ checkComposeBox (isShown: true );
759
+ });
760
+
761
+ testWidgets ('channel policy becomes stricter -> compose box is replaced with the banner' , (tester) async {
762
+ final selfUser = eg.user (role: UserRole .guest);
763
+ final channel = eg.stream (streamId: 1 ,
764
+ channelPostPolicy: ChannelPostPolicy .any);
765
+
766
+ await prepareComposeBox (tester,
767
+ narrow: const ChannelNarrow (1 ),
768
+ selfUser: selfUser,
769
+ streams: [channel]);
770
+ checkComposeBox (isShown: true );
771
+
772
+ await store.handleEvent (eg.channelUpdateEvent (channel,
773
+ property: ChannelPropertyName .channelPostPolicy,
774
+ value: ChannelPostPolicy .fullMembers));
775
+ await tester.pump ();
776
+ checkComposeBox (isShown: false );
777
+ });
778
+
779
+ testWidgets ('channel policy becomes less strict -> banner is replaced with the compose box' , (tester) async {
780
+ final selfUser = eg.user (role: UserRole .moderator);
781
+ final channel = eg.stream (streamId: 1 ,
782
+ channelPostPolicy: ChannelPostPolicy .administrators);
783
+
784
+ await prepareComposeBox (tester,
785
+ narrow: const ChannelNarrow (1 ),
786
+ selfUser: selfUser,
787
+ streams: [channel]);
788
+ checkComposeBox (isShown: false );
789
+
790
+ await store.handleEvent (eg.channelUpdateEvent (channel,
791
+ property: ChannelPropertyName .channelPostPolicy,
792
+ value: ChannelPostPolicy .moderators));
793
+ await tester.pump ();
794
+ checkComposeBox (isShown: true );
795
+ });
796
+ });
689
797
});
690
798
}
0 commit comments