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

Add E2E test of MessageEditHistoryDialog #10406

Merged
merged 21 commits into from
Mar 30, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
231 changes: 225 additions & 6 deletions cypress/e2e/editing/editing.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ limitations under the License.
import type { EventType, MsgType } from "matrix-js-sdk/src/@types/event";
import type { ISendEventResponse } from "matrix-js-sdk/src/@types/requests";
import type { IContent } from "matrix-js-sdk/src/models/event";
import { SettingLevel } from "../../../src/settings/SettingLevel";
import { HomeserverInstance } from "../../plugins/utils/homeserver";
import Chainable = Cypress.Chainable;

Expand All @@ -41,12 +42,46 @@ function mkPadding(n: number): IContent {

describe("Editing", () => {
let homeserver: HomeserverInstance;
let roomId: string;

// Edit "Message"
const editLastMessage = (edit: string) => {
cy.get(".mx_EventTile_last").realHover();
cy.get(".mx_EventTile_last .mx_MessageActionBar_optionsButton", { timeout: 1000 })
.should("exist")
.realHover()
.get('.mx_EventTile_last [aria-label="Edit"]')
.click({ force: false });
cy.get(".mx_BasicMessageComposer_input").type(`{selectAll}{del}${edit}{enter}`);
};

const clickEditedMessage = (edited: string) => {
// Assert that the message was edited
cy.contains(".mx_EventTile", edited)
.should("exist")
.within(() => {
// Click to display the message edit history dialog
cy.contains(".mx_EventTile_edited", "(edited)").click();
});
};

const clickButtonViewSource = () => {
// Assert that "View Source" button is rendered and click it
cy.get(".mx_EventTile .mx_EventTile_line")
.realHover()
.contains(".mx_AccessibleButton", "View Source")
.should("exist")
.click();
};

beforeEach(() => {
cy.startHomeserver("default").then((data) => {
homeserver = data;
cy.initTestUser(homeserver, "Edith").then(() => {
cy.injectAxe();
cy.createRoom({ name: "Test room" }).then((_room1Id) => {
roomId = _room1Id;
}),
cy.injectAxe();
});
});
});
Expand All @@ -55,14 +90,198 @@ describe("Editing", () => {
cy.stopHomeserver(homeserver);
});

it("should close the composer when clicking save after making a change and undoing it", () => {
cy.createRoom({ name: "Test room" }).as("roomId");
it("should render and interact with the message edit history dialog", () => {
// Click the "Remove" button on the message edit history dialog
const clickButtonRemove = () => {
cy.get(".mx_EventTile_line").realHover().contains(".mx_AccessibleButton", "Remove").click({ force: false });
};

cy.visit("/#/room/" + roomId);

// Send "Message"
sendEvent(roomId);

cy.get(".mx_RoomView_MessageList").within(() => {
// Edit "Message" to "Massage"
editLastMessage("Massage");

// Assert that the edit label is visible
cy.get(".mx_EventTile_edited").should("be.visible");

clickEditedMessage("Massage");
});

cy.get(".mx_Dialog").within(() => {
// Assert that the message edit history dialog is rendered
cy.get(".mx_MessageEditHistoryDialog").within(() => {
// Assert CSS styles which are difficult or cannot be detected with snapshots are applied as expected
cy.get("li").should("have.css", "clear", "both");
cy.get(".mx_EventTile .mx_MessageTimestamp")
.should("have.css", "position", "absolute")
.should("have.css", "inset-inline-start", "0px")
.should("have.css", "text-align", "center");
// Assert that monospace characters can fill the content line as expected
cy.get(".mx_EventTile .mx_EventTile_content").should("have.css", "margin-inline-end", "0px");
Copy link
Contributor Author

@luixxiul luixxiul Mar 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line also should be checked because this margin value is specific to the message edit history dialog (See this line for comparison) and it is difficult to detect a regression with a snapshot because text-align is not set to justify, which makes it hard to judge whether the margin setting is really respected or a line is wrapped just because of the length of a word, even if we use a sentence long enough to be wrapped as an example.

For comparison, languages such as Chinese/Japanese/Korean do not have the concept of wrapping a sentence based on the word length, nor hyphenation, therefore any sentence uses every space available inside a line before being wrapped (ie. the same number of characters on each line inside a paragraph), which essentially means that every wrapped sentence is aligned to be justified. This it not perfectly correct, but you could regard CJK characters as monospace in most cases. Therefore checking the margin here makes sense for those languages.

This also means that using a sample text in CJK would make it unnecessary to check this value, but I think it is not practical, considering the possibility of outputting tofu characters if your computer does not have a CJK font, etc., which increases the possibility of flaky test results.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1336573 adds a comment about this.


// Assert that zero block start padding is applied to mx_EventTile as expected
// See: .mx_EventTile on _EventTile.pcss
cy.get(".mx_EventTile").should("have.css", "padding-block-start", "0px");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for #9033 (checking EventTiles on MessageEditHistoryDialog), one of the main aims of this PR.


// Assert that the date separator is rendered at the top
cy.get("li:nth-child(1) .mx_DateSeparator").within(() => {
cy.get("h2").should("have.text", "Today");
});

cy.get<string>("@roomId").then((roomId) => {
sendEvent(roomId);
cy.visit("/#/room/" + roomId);
// Assert that the edited message is rendered under the date separator
cy.get("li:nth-child(2) .mx_EventTile").within(() => {
// Assert that the edited message body consists of both deleted character and inserted character
// Above the first "e" of "Message" was replaced with "a"
cy.get(".mx_EventTile_content .mx_EventTile_body").should("have.text", "Meassage");

cy.get(".mx_EventTile_content .mx_EventTile_body").within(() => {
cy.get(".mx_EditHistoryMessage_deletion").should("have.text", "e");
cy.get(".mx_EditHistoryMessage_insertion").should("have.text", "a");
});
});

// Assert that the original message is rendered at the bottom
cy.get("li:nth-child(3) .mx_EventTile").within(() => {
cy.get(".mx_EventTile_content .mx_EventTile_body").should("have.text", "Message");
});
});
});

// Exclude timestamps from a snapshot
const percyCSS = ".mx_MessageTimestamp { visibility: hidden !important; }";

// Take a snapshot of the dialog
cy.get(".mx_Dialog_wrapper").percySnapshotElement("Message edit history dialog", { percyCSS });

cy.get(".mx_Dialog").within(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to join the three within?

Something like

cy.get(".mx_Dialog .mx_MessageEditHistoryDialog li:nth-child(2) .mx_EventTile").within(() => {

(may also apply to other places)

This comment was marked as outdated.

Copy link
Contributor Author

@luixxiul luixxiul Mar 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found there were cases which actually should be joined. Working on it..

Copy link
Contributor Author

@luixxiul luixxiul Mar 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit 5c58427 addresses this. It does not join every within for readability and to clarify the relationship between mx_TextInputDialog and mx_MessageEditHistoryDialog under mx_Dialog.

cy.get(".mx_MessageEditHistoryDialog li:nth-child(2) .mx_EventTile").within(() => {
cy.get(".mx_EventTile_content .mx_EventTile_body").should("have.text", "Meassage");

// Click the "Remove" button again
clickButtonRemove();
});

// Do nothing and close the dialog to confirm that the message edit history dialog is rendered
cy.get(".mx_TextInputDialog").closeDialog();

// Assert that the message edit history dialog is rendered again after it was closed
cy.get(".mx_MessageEditHistoryDialog li:nth-child(2) .mx_EventTile").within(() => {
cy.get(".mx_EventTile_content .mx_EventTile_body").should("have.text", "Meassage");

// Click the "Remove" button again
clickButtonRemove();
});

// This time remove the message really
cy.get(".mx_TextInputDialog").within(() => {
cy.get(".mx_TextInputDialog_input").type("This is a test."); // Reason
cy.contains("[data-testid='dialog-primary-button']", "Remove").click();
});

// Assert that the message edit history dialog is rendered again
cy.get(".mx_MessageEditHistoryDialog").within(() => {
// Assert that the date is rendered
cy.get("li:nth-child(1) .mx_DateSeparator").within(() => {
cy.get("h2").should("have.text", "Today");
});

// Assert that the original message is rendered under the date on the dialog
cy.get("li:nth-child(2) .mx_EventTile").within(() => {
cy.get(".mx_EventTile_content .mx_EventTile_body").should("have.text", "Message");
});

// Assert that the edited message is gone
cy.contains(".mx_EventTile_content .mx_EventTile_body", "Meassage").should("not.exist");

cy.closeDialog();
});
});

// Assert that the main timeline is rendered
cy.get(".mx_RoomView_MessageList").within(() => {
cy.get(".mx_EventTile_last").within(() => {
// Assert that the placeholder is rendered
cy.contains(".mx_RedactedBody", "Message deleted");
});
});
});

it("should render 'View Source' button in developer mode on the message edit history dialog", () => {
cy.visit("/#/room/" + roomId);

// Send "Message"
sendEvent(roomId);

cy.get(".mx_RoomView_MessageList").within(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you try to extract shared code to their own functions?
This would make the tests better maintainable.

Copy link
Contributor Author

@luixxiul luixxiul Mar 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added some functions:

  • editLastMessage()
  • clickButtonRemove()
  • clickButtonViewSource()
  • clickEditedMessage()

// Edit "Message" to "Massage"
editLastMessage("Massage");

// Assert that the edit label is visible
cy.get(".mx_EventTile_edited").should("be.visible");

clickEditedMessage("Massage");
});

cy.get(".mx_Dialog").within(() => {
// Assert that the original message is rendered
cy.get(".mx_MessageEditHistoryDialog li:nth-child(3)").within(() => {
// Assert that "View Source" is not rendered
cy.get(".mx_EventTile .mx_EventTile_line")
.realHover()
.contains(".mx_AccessibleButton", "View Source")
.should("not.exist");
});

cy.closeDialog();
});

// Enable developer mode
cy.setSettingValue("developerMode", null, SettingLevel.ACCOUNT, true);

cy.get(".mx_RoomView_MessageList").within(() => {
clickEditedMessage("Massage");
});

cy.get(".mx_Dialog").within(() => {
// Assert that the edited message is rendered
cy.get(".mx_MessageEditHistoryDialog li:nth-child(2)").within(() => {
// Assert that "Remove" button for the original message is rendered
cy.get(".mx_EventTile .mx_EventTile_line")
.realHover()
.contains(".mx_AccessibleButton", "Remove")
.should("exist");

clickButtonViewSource();
});

// Assert that view source dialog is rendered and close the dialog
cy.get(".mx_ViewSource").closeDialog();

// Assert that the original message is rendered
cy.get(".mx_MessageEditHistoryDialog li:nth-child(3)").within(() => {
// Assert that "Remove" button for the original message does not exist
cy.get(".mx_EventTile .mx_EventTile_line")
.realHover()
.contains(".mx_AccessibleButton", "Remove")
.should("not.exist");

clickButtonViewSource();
});

// Assert that view source dialog is rendered and close the dialog
cy.get(".mx_ViewSource").closeDialog();
});
});

it("should close the composer when clicking save after making a change and undoing it", () => {
cy.visit("/#/room/" + roomId);

sendEvent(roomId);

// Edit message
cy.contains(".mx_RoomView_body .mx_EventTile .mx_EventTile_line", "Message").within(() => {
cy.get('[aria-label="Edit"]').click({ force: true }); // Cypress has no ability to hover
Expand Down