Skip to content

Commit e5bcc1d

Browse files
committed
chore: cover all reaction cases for offline mode
1 parent 7bee59b commit e5bcc1d

File tree

1 file changed

+250
-2
lines changed

1 file changed

+250
-2
lines changed

package/src/__tests__/offline-support/offline-feature.js

Lines changed: 250 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,10 @@ export const Generic = () => {
127127
const createChannel = (messagesOverride) => {
128128
const id = uuidv4();
129129
const cid = `messaging:${id}`;
130-
const begin = getRandomInt(0, allUsers.length - 2); // begin shouldn't be the end of users.length
131-
const end = getRandomInt(begin + 1, allUsers.length - 1);
130+
// always guarantee at least 2 members for ease of use; cases that need to test specific behaviour
131+
// for 1 or 0 member channels should explicitly generate them.
132+
const begin = getRandomInt(0, allUsers.length - 3); // begin shouldn't be the end of users.length
133+
const end = getRandomInt(begin + 2, allUsers.length - 1);
132134
const usersForMembers = allUsers.slice(begin, end);
133135
const members = usersForMembers.map((user) =>
134136
generateMember({
@@ -785,6 +787,155 @@ export const Generic = () => {
785787
});
786788
});
787789

790+
it('should correctly add multiple reactions to the DB', async () => {
791+
useMockedApis(chatClient, [queryChannelsApi(channels)]);
792+
renderComponent();
793+
act(() => dispatchConnectionChangedEvent(chatClient));
794+
await act(async () => await chatClient.offlineDb.syncManager.invokeSyncStatusListeners(true));
795+
await waitFor(() => expect(screen.getByTestId('channel-list')).toBeTruthy());
796+
797+
const targetChannel = channels[getRandomInt(0, channels.length - 1)];
798+
const targetMessage =
799+
targetChannel.messages[getRandomInt(0, targetChannel.messages.length - 1)];
800+
const reactionMember =
801+
targetChannel.members[getRandomInt(0, targetChannel.members.length - 1)];
802+
const someOtherMember = targetChannel.members.filter(
803+
(member) => reactionMember.user.id !== member.user.id,
804+
)[getRandomInt(0, targetChannel.members.length - 2)];
805+
806+
const newReactions = [
807+
generateReaction({
808+
message_id: targetMessage.id,
809+
type: 'wow',
810+
user: reactionMember.user,
811+
}),
812+
generateReaction({
813+
message_id: targetMessage.id,
814+
type: 'wow',
815+
user: someOtherMember.user,
816+
}),
817+
generateReaction({
818+
message_id: targetMessage.id,
819+
type: 'love',
820+
user: reactionMember.user,
821+
}),
822+
];
823+
const messageWithNewReactionBase = {
824+
...targetMessage,
825+
latest_reactions: [...targetMessage.latest_reactions],
826+
};
827+
const newLatestReactions = [];
828+
829+
newReactions.forEach((newReaction) => {
830+
newLatestReactions.push(newReaction);
831+
const messageWithNewReaction = {
832+
...messageWithNewReactionBase,
833+
latest_reactions: [...messageWithNewReactionBase.latest_reactions, ...newLatestReactions],
834+
};
835+
act(() =>
836+
dispatchReactionNewEvent(
837+
chatClient,
838+
newReaction,
839+
messageWithNewReaction,
840+
targetChannel.channel,
841+
),
842+
);
843+
});
844+
845+
const finalReactionCount =
846+
messageWithNewReactionBase.latest_reactions.length +
847+
newReactions.filter(
848+
(newReaction) =>
849+
!messageWithNewReactionBase.latest_reactions.some(
850+
(initialReaction) =>
851+
initialReaction.type === newReaction.type &&
852+
initialReaction.user.id === newReaction.user.id,
853+
),
854+
).length;
855+
856+
await waitFor(async () => {
857+
const reactionsRows = await BetterSqlite.selectFromTable('reactions');
858+
const matchingReactionsRows = reactionsRows.filter(
859+
(r) => r.messageId === messageWithNewReactionBase.id,
860+
);
861+
862+
expect(matchingReactionsRows.length).toBe(finalReactionCount);
863+
newReactions.forEach((newReaction) => {
864+
expect(
865+
matchingReactionsRows.filter(
866+
(reaction) =>
867+
reaction.type === newReaction.type && reaction.userId === newReaction.user.id,
868+
).length,
869+
).toBe(1);
870+
});
871+
});
872+
});
873+
874+
it('should gracefully handle multiple reaction.new events of the same type for the same user', async () => {
875+
useMockedApis(chatClient, [queryChannelsApi(channels)]);
876+
renderComponent();
877+
act(() => dispatchConnectionChangedEvent(chatClient));
878+
await act(async () => await chatClient.offlineDb.syncManager.invokeSyncStatusListeners(true));
879+
await waitFor(() => expect(screen.getByTestId('channel-list')).toBeTruthy());
880+
881+
const targetChannel = channels[getRandomInt(0, channels.length - 1)];
882+
const targetMessage =
883+
targetChannel.messages[getRandomInt(0, targetChannel.messages.length - 1)];
884+
const reactionMember =
885+
targetChannel.members[getRandomInt(0, targetChannel.members.length - 1)];
886+
887+
const newReactions = [
888+
generateReaction({
889+
message_id: targetMessage.id,
890+
type: 'wow',
891+
user: reactionMember.user,
892+
}),
893+
generateReaction({
894+
message_id: targetMessage.id,
895+
type: 'wow',
896+
user: reactionMember.user,
897+
}),
898+
generateReaction({
899+
message_id: targetMessage.id,
900+
type: 'wow',
901+
user: reactionMember.user,
902+
}),
903+
];
904+
const messageWithNewReactionBase = {
905+
...targetMessage,
906+
latest_reactions: [...targetMessage.latest_reactions],
907+
};
908+
const newLatestReactions = [];
909+
910+
newReactions.forEach((newReaction) => {
911+
newLatestReactions.push(newReaction);
912+
const messageWithNewReaction = {
913+
...messageWithNewReactionBase,
914+
latest_reactions: [...messageWithNewReactionBase.latest_reactions, ...newLatestReactions],
915+
};
916+
act(() =>
917+
dispatchReactionNewEvent(
918+
chatClient,
919+
newReaction,
920+
messageWithNewReaction,
921+
targetChannel.channel,
922+
),
923+
);
924+
});
925+
926+
await waitFor(async () => {
927+
const reactionsRows = await BetterSqlite.selectFromTable('reactions');
928+
const matchingReactionsRows = reactionsRows.filter(
929+
(r) =>
930+
r.type === 'wow' &&
931+
r.userId === reactionMember.user.id &&
932+
r.messageId === messageWithNewReactionBase.id,
933+
);
934+
935+
expect(matchingReactionsRows.length).toBe(1);
936+
});
937+
});
938+
788939
it('should remove a reaction from DB when reaction is deleted', async () => {
789940
useMockedApis(chatClient, [queryChannelsApi(channels)]);
790941

@@ -877,6 +1028,103 @@ export const Generic = () => {
8771028
});
8781029
});
8791030

1031+
it('should correctly upsert reactions when enforce_unique is true', async () => {
1032+
useMockedApis(chatClient, [queryChannelsApi(channels)]);
1033+
renderComponent();
1034+
act(() => dispatchConnectionChangedEvent(chatClient));
1035+
await act(async () => await chatClient.offlineDb.syncManager.invokeSyncStatusListeners(true));
1036+
await waitFor(() => expect(screen.getByTestId('channel-list')).toBeTruthy());
1037+
1038+
const targetChannel = channels[getRandomInt(0, channels.length - 1)];
1039+
const targetMessage =
1040+
targetChannel.messages[getRandomInt(0, targetChannel.messages.length - 1)];
1041+
const reactionMember =
1042+
targetChannel.members[getRandomInt(0, targetChannel.members.length - 1)];
1043+
1044+
const newReactions = [
1045+
generateReaction({
1046+
message_id: targetMessage.id,
1047+
type: 'wow',
1048+
user: reactionMember.user,
1049+
}),
1050+
generateReaction({
1051+
message_id: targetMessage.id,
1052+
type: 'love',
1053+
user: reactionMember.user,
1054+
}),
1055+
];
1056+
const messageWithNewReactionBase = {
1057+
...targetMessage,
1058+
latest_reactions: [...targetMessage.latest_reactions],
1059+
};
1060+
const newLatestReactions = [];
1061+
1062+
newReactions.forEach((newReaction) => {
1063+
newLatestReactions.push(newReaction);
1064+
const messageWithNewReaction = {
1065+
...messageWithNewReactionBase,
1066+
latest_reactions: [...messageWithNewReactionBase.latest_reactions, ...newLatestReactions],
1067+
};
1068+
act(() =>
1069+
dispatchReactionNewEvent(
1070+
chatClient,
1071+
newReaction,
1072+
messageWithNewReaction,
1073+
targetChannel.channel,
1074+
),
1075+
);
1076+
});
1077+
1078+
await waitFor(async () => {
1079+
const reactionsRows = await BetterSqlite.selectFromTable('reactions');
1080+
const matchingReactionsRows = reactionsRows.filter(
1081+
(r) =>
1082+
r.messageId === messageWithNewReactionBase.id && r.userId === reactionMember.user.id,
1083+
);
1084+
1085+
expect(matchingReactionsRows.length).toBe(2);
1086+
newReactions.forEach((newReaction) => {
1087+
expect(
1088+
matchingReactionsRows.filter(
1089+
(reaction) =>
1090+
reaction.type === newReaction.type && reaction.userId === newReaction.user.id,
1091+
).length,
1092+
).toBe(1);
1093+
});
1094+
});
1095+
1096+
const uniqueReaction = generateReaction({
1097+
message_id: targetMessage.id,
1098+
type: 'like',
1099+
user: reactionMember.user,
1100+
});
1101+
const messageWithNewReaction = {
1102+
...targetMessage,
1103+
latest_reactions: [...targetMessage.latest_reactions, uniqueReaction],
1104+
};
1105+
1106+
act(() =>
1107+
dispatchReactionUpdatedEvent(
1108+
chatClient,
1109+
uniqueReaction,
1110+
messageWithNewReaction,
1111+
targetChannel.channel,
1112+
),
1113+
);
1114+
1115+
await waitFor(async () => {
1116+
const reactionsRows = await BetterSqlite.selectFromTable('reactions');
1117+
const matchingReactionsRows = reactionsRows.filter(
1118+
(r) =>
1119+
r.type === uniqueReaction.type &&
1120+
r.userId === reactionMember.user.id &&
1121+
r.messageId === messageWithNewReaction.id,
1122+
);
1123+
1124+
expect(matchingReactionsRows.length).toBe(1);
1125+
});
1126+
});
1127+
8801128
it('should add a member to DB when a new member is added to channel', async () => {
8811129
useMockedApis(chatClient, [queryChannelsApi(channels)]);
8821130
renderComponent();

0 commit comments

Comments
 (0)