Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
Cypress tests for redactions in threads (#11594)
Browse files Browse the repository at this point in the history
* Tests for redacting messages not increasing unreads

* Comment explaining tips for writing high level rr tests

* Test for restarting with a receipt pointing at a redacted thread root

* Two failing tests for counting correctly when a thread message was redacted

* Test for reading a thread containing an earlier redaction

* Failing tests for redacted messages in threads
  • Loading branch information
andybalaam authored Sep 18, 2023
1 parent 5a3c32e commit e887c6d
Showing 1 changed file with 262 additions and 11 deletions.
273 changes: 262 additions & 11 deletions cypress/e2e/read-receipts/high-level.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,29 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

/*
* # High Level Read Receipt Tests
*
* Tips for writing these tests:
*
* * Break up your tests into the smallest test case possible. The purpose of
* these tests is to understand hard-to-find bugs, so small tests are necessary.
* We know that Cypress recommends combining tests together for performance, but
* that will frustrate our goals here. (We will need to find a different way to
* reduce CI time.)
*
* * Try to assert something after every action, to make sure it has completed.
* E.g.:
* markAsRead(room2);
* assertRead(room2);
* You should especially follow this rule if you are jumping to a different
* room or similar straight afterwards.
*
* * Use assertStillRead() if you are asserting something is read when it was
* also read before. This waits a little while to make sure you're not getting a
* false positive.
*/

/// <reference types="cypress" />

import type { MatrixClient, MatrixEvent, Room, IndexedDBStore } from "matrix-js-sdk/src/matrix";
Expand Down Expand Up @@ -137,6 +160,16 @@ describe("Read receipts", () => {
cy.get(".mx_ThreadView_timelinePanelWrapper", { log: false }).should("have.length", 1);
}

/**
* Close the threads panel. (Actually, close any right panel, but for these
* tests we only open the threads panel.)
*/
function closeThreadsPanel() {
cy.log("Close threads panel");
cy.get(".mx_RightPanel").findByTitle("Close").click();
cy.get(".mx_RightPanel").should("not.exist");
}

function sendMessageAsClient(cli: MatrixClient, room: string, messages: Message[]) {
findRoomByName(room).then(async ({ roomId }) => {
const room = cli.getRoom(roomId);
Expand Down Expand Up @@ -1618,24 +1651,242 @@ describe("Read receipts", () => {
});

describe("in threads", () => {
// One of the following two must be right:
it.skip("Redacting the threaded message pointed to by my receipt leaves the room read", () => {});
it.skip("Redacting a threaded message after it was read makes the room unread", () => {});
it("Redacting the threaded message pointed to by my receipt leaves the room read", () => {
// Given I have some threads
goTo(room1);
receiveMessages(room2, [
"Root",
threadedOff("Root", "ThreadMsg1"),
threadedOff("Root", "ThreadMsg2"),
"Root2",
threadedOff("Root2", "Root2->A"),
]);
assertUnread(room2, 5);

// And I have read them
goTo(room2);
assertUnreadThread("Root");
openThread("Root");
assertUnreadLessThan(room2, 4);
openThread("Root2");
assertRead(room2);
closeThreadsPanel();
goTo(room1);
assertRead(room2);

// When the latest message in a thread is redacted
receiveMessages(room2, [redactionOf("ThreadMsg2")]);

// Then the room and thread are still read
assertStillRead(room2);
goTo(room2);
assertReadThread("Root");
});

// XXX: fails because the unread count is still 1 when it should be 0 (this is a genuine stuck unread case)
it.skip("Reading an unread thread after a redaction of the latest message makes it read", () => {
// Given an unread thread where the latest message was redacted
goTo(room1);
receiveMessages(room2, ["Root", threadedOff("Root", "ThreadMsg1"), threadedOff("Root", "ThreadMsg2")]);
assertUnread(room2, 3);
receiveMessages(room2, [redactionOf("ThreadMsg2")]);
assertUnread(room2, 2);
goTo(room2);
assertUnreadThread("Root");

// When I read the thread
openThread("Root");
assertRead(room2);
closeThreadsPanel();
goTo(room1);

// Then the thread is read
assertRead(room2);
goTo(room2);
assertReadThread("Root");
});
// XXX: fails because the unread count is still 1 when it should be 0
it.skip("Reading an unread thread after a redaction of the latest message makes it read after restart", () => {
// Given a redacted message is not counted in the unread count
goTo(room1);
receiveMessages(room2, ["Root", threadedOff("Root", "ThreadMsg1"), threadedOff("Root", "ThreadMsg2")]);
assertUnread(room2, 3);
receiveMessages(room2, [redactionOf("ThreadMsg2")]);
assertUnread(room2, 2);
goTo(room2);
assertUnreadThread("Root");
openThread("Root");
assertRead(room2);
closeThreadsPanel();
goTo(room1);
assertRead(room2);
goTo(room2);
assertReadThread("Root");

// When I restart
saveAndReload();

// Then the room is still read
assertRead(room2);
});
// XXX: fails because the unread count is still 1 when it should be 0
it.skip("Reading an unread thread after a redaction of an older message makes it read", () => {
// Given an unread thread where an older message was redacted
goTo(room1);
receiveMessages(room2, ["Root", threadedOff("Root", "ThreadMsg1"), threadedOff("Root", "ThreadMsg2")]);
assertUnread(room2, 3);
receiveMessages(room2, [redactionOf("ThreadMsg1")]);
assertUnread(room2, 2);
goTo(room2);
assertUnreadThread("Root");

// When I read the thread
openThread("Root");
assertRead(room2);
closeThreadsPanel();
goTo(room1);

// Then the thread is read
assertRead(room2);
goTo(room2);
assertReadThread("Root");
});
// XXX: fails because the room has an unread dot after I marked it as read
it.skip("Marking an unread thread as read after a redaction makes it read", () => {
// Given an unread thread where an older message was redacted
goTo(room1);
receiveMessages(room2, ["Root", threadedOff("Root", "ThreadMsg1"), threadedOff("Root", "ThreadMsg2")]);
assertUnread(room2, 3);
receiveMessages(room2, [redactionOf("ThreadMsg1")]);
assertUnread(room2, 2);

it.skip("Reading an unread thread after a redaction of the latest message makes it read", () => {});
it.skip("Reading an unread thread after a redaction of an older message makes it read", () => {});
it.skip("Marking an unread thread as read after a redaction makes it read", () => {});
it.skip("Sending and redacting a message after marking the thread as read makes it unread", () => {});
it.skip("?? Redacting a message after marking the thread as read makes it unread", () => {});
it.skip("Reacting to a redacted message leaves the thread read", () => {});
it.skip("Editing a redacted message leaves the thread read", () => {});
// When I mark the room as read
markAsRead(room2);
assertRead(room2);

// Then the thread is read
assertRead(room2);
goTo(room2);
assertReadThread("Root");
});
// XXX: fails because the room has an unread dot after I marked it as read
it.skip("Sending and redacting a message after marking the thread as read leaves it read", () => {
// Given a thread exists and is marked as read
goTo(room1);
receiveMessages(room2, ["Root", threadedOff("Root", "ThreadMsg1"), threadedOff("Root", "ThreadMsg2")]);
assertUnread(room2, 3);
markAsRead(room2);
assertRead(room2);

// When I send and redact a message
receiveMessages(room2, [threadedOff("Root", "Msg3")]);
assertUnread(room2, 1);
receiveMessages(room2, [redactionOf("Msg3")]);

// Then the room and thread are read
assertRead(room2);
goTo(room2);
assertReadThread("Root");
});
// XXX: fails because the room has an unread dot after I marked it as read
it.skip("Redacting a message after marking the thread as read leaves it read", () => {
// Given a thread exists and is marked as read
goTo(room1);
receiveMessages(room2, ["Root", threadedOff("Root", "ThreadMsg1"), threadedOff("Root", "ThreadMsg2")]);
assertUnread(room2, 3);
markAsRead(room2);
assertRead(room2);

// When I redact a message
receiveMessages(room2, [redactionOf("ThreadMsg1")]);

// Then the room and thread are read
assertRead(room2);
goTo(room2);
assertReadThread("Root");
});
// TODO: Doesn't work because the test setup can't (yet) find the ID of a redacted message
it.skip("Reacting to a redacted message leaves the thread read", () => {
// Given a message in a thread was redacted and everything is read
goTo(room1);
receiveMessages(room2, ["Root", threadedOff("Root", "Msg2"), threadedOff("Root", "Msg3")]);
receiveMessages(room2, [redactionOf("Msg2")]);
assertUnread(room2, 2);
goTo(room2);
assertUnread(room2, 1);
openThread("Root");
assertRead(room2);
goTo(room1);

// When we receive a reaction to the redacted event
// TODO: doesn't work yet because we need to be able to look up
// the ID of Msg2 even though it has now disappeared from the
// timeline.
receiveMessages(room2, [reactionTo(room2, "Msg2")]);

// Then the room is unread
assertStillRead(room2);
});
// TODO: Doesn't work because the test setup can't (yet) find the ID of a redacted message
it.skip("Editing a redacted message leaves the thread read", () => {
// Given a message in a thread was redacted and everything is read
goTo(room1);
receiveMessages(room2, ["Root", threadedOff("Root", "Msg2"), threadedOff("Root", "Msg3")]);
receiveMessages(room2, [redactionOf("Msg2")]);
assertUnread(room2, 2);
goTo(room2);
assertUnread(room2, 1);
openThread("Root");
assertRead(room2);
goTo(room1);

// When we receive an edit of the redacted message
// TODO: doesn't work yet because we need to be able to look up
// the ID of Msg2 even though it has now disappeared from the
// timeline.
receiveMessages(room2, [editOf("Msg2", "New Msg2")]);

// Then the room is unread
assertStillRead(room2);
});

it.skip("?? Reading a reaction to a redacted message marks the thread as read", () => {});
it.skip("?? Reading an edit of a redacted message marks the thread as read", () => {});
it.skip("Reading a reply to a redacted message marks the thread as read", () => {});
it.skip("Reading a thread root when its only message has been redacted leaves the room read", () => {});

it.skip("A thread with an unread redaction is still unread after restart", () => {});
it.skip("A thread with a read redaction is still read after restart", () => {});
it("A thread with a read redaction is still read after restart", () => {
// Given my receipt points at a redacted thread message
goTo(room1);
receiveMessages(room2, [
"Root",
threadedOff("Root", "ThreadMsg1"),
threadedOff("Root", "ThreadMsg2"),
"Root2",
threadedOff("Root2", "Root2->A"),
]);
assertUnread(room2, 5);
goTo(room2);
assertUnreadThread("Root");
openThread("Root");
assertUnreadLessThan(room2, 4);
openThread("Root2");
assertRead(room2);
closeThreadsPanel();
goTo(room1);
assertRead(room2);
receiveMessages(room2, [redactionOf("ThreadMsg2")]);
assertStillRead(room2);
goTo(room2);
assertReadThread("Root");

// When I restart
saveAndReload();

// Then the room is still read
assertRead(room2);
});
it.skip("A thread with an unread reply to a redacted message is still unread after restart", () => {});
it.skip("A thread with a read replt to a redacted message is still read after restart", () => {});
});
Expand Down

0 comments on commit e887c6d

Please sign in to comment.