mirror of
https://github.com/Novaloop-AG/healthchat-web.git
synced 2026-11-03 06:55:44 +00:00
Absorb the matrix-react-sdk repository (#28192)
Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com> Co-authored-by: github-merge-queue <github-merge-queue@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Florian Duros <florian.duros@ormaz.fr> Co-authored-by: Kim Brose <kim.brose@nordeck.net> Co-authored-by: Florian Duros <florianduros@element.io> Co-authored-by: R Midhun Suresh <hi@midhun.dev> Co-authored-by: dbkr <986903+dbkr@users.noreply.github.com> Co-authored-by: ElementRobot <releases@riot.im> Co-authored-by: dbkr <dbkr@users.noreply.github.com> Co-authored-by: David Baker <dbkr@users.noreply.github.com> Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Co-authored-by: David Langley <davidl@element.io> Co-authored-by: Michael Weimann <michaelw@matrix.org> Co-authored-by: Timshel <Timshel@users.noreply.github.com> Co-authored-by: Sahil Silare <32628578+sahil9001@users.noreply.github.com> Co-authored-by: Will Hunt <will@half-shot.uk> Co-authored-by: Hubert Chathi <hubert@uhoreg.ca> Co-authored-by: Andrew Ferrazzutti <andrewf@element.io> Co-authored-by: Robin <robin@robin.town> Co-authored-by: Tulir Asokan <tulir@maunium.net>
This commit is contained in:
committed by
GitHub
parent
2b99496025
commit
c05c429803
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { test, expect } from "../../element-web-test";
|
||||
import { SettingLevel } from "../../../src/settings/SettingLevel";
|
||||
|
||||
const CtrlOrMeta = process.platform === "darwin" ? "Meta" : "Control";
|
||||
|
||||
test.describe("Composer", () => {
|
||||
test.use({
|
||||
displayName: "Janet",
|
||||
});
|
||||
|
||||
test.use({
|
||||
room: async ({ app, user }, use) => {
|
||||
const roomId = await app.client.createRoom({ name: "Composing Room" });
|
||||
await app.viewRoomByName("Composing Room");
|
||||
await use({ roomId });
|
||||
},
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ room }) => {}); // trigger room fixture
|
||||
|
||||
test.describe("CIDER", () => {
|
||||
test("sends a message when you click send or press Enter", async ({ page }) => {
|
||||
const composer = page.getByRole("textbox", { name: "Send a message…" });
|
||||
|
||||
// Type a message
|
||||
await composer.pressSequentially("my message 0");
|
||||
// It has not been sent yet
|
||||
await expect(page.locator(".mx_EventTile_body", { hasText: "my message 0" })).not.toBeVisible();
|
||||
|
||||
// Click send
|
||||
await page.getByRole("button", { name: "Send message" }).click();
|
||||
// It has been sent
|
||||
await expect(
|
||||
page.locator(".mx_EventTile_last .mx_EventTile_body", { hasText: "my message 0" }),
|
||||
).toBeVisible();
|
||||
|
||||
// Type another and press Enter afterward
|
||||
await composer.pressSequentially("my message 1");
|
||||
await composer.press("Enter");
|
||||
// It was sent
|
||||
await expect(
|
||||
page.locator(".mx_EventTile_last .mx_EventTile_body", { hasText: "my message 1" }),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("can write formatted text", async ({ page }) => {
|
||||
const composer = page.getByRole("textbox", { name: "Send a message…" });
|
||||
|
||||
await composer.pressSequentially("my bold");
|
||||
await composer.press(`${CtrlOrMeta}+KeyB`);
|
||||
await composer.pressSequentially(" message");
|
||||
await page.getByRole("button", { name: "Send message" }).click();
|
||||
// Note: both "bold" and "message" are bold, which is probably surprising
|
||||
await expect(page.locator(".mx_EventTile_body strong", { hasText: "bold message" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("should allow user to input emoji via graphical picker", async ({ page, app }) => {
|
||||
await app.getComposer(false).getByRole("button", { name: "Emoji" }).click();
|
||||
|
||||
await page.getByTestId("mx_EmojiPicker").locator(".mx_EmojiPicker_item", { hasText: "😇" }).click();
|
||||
|
||||
await page.locator(".mx_ContextualMenu_background").click(); // Close emoji picker
|
||||
await page.getByRole("textbox", { name: "Send a message…" }).press("Enter"); // Send message
|
||||
|
||||
await expect(page.locator(".mx_EventTile_body", { hasText: "😇" })).toBeVisible();
|
||||
});
|
||||
|
||||
test.describe("when Control+Enter is required to send", () => {
|
||||
test.beforeEach(async ({ app }) => {
|
||||
await app.settings.setValue("MessageComposerInput.ctrlEnterToSend", null, SettingLevel.ACCOUNT, true);
|
||||
});
|
||||
|
||||
test("only sends when you press Control+Enter", async ({ page }) => {
|
||||
const composer = page.getByRole("textbox", { name: "Send a message…" });
|
||||
// Type a message and press Enter
|
||||
await composer.pressSequentially("my message 3");
|
||||
await composer.press("Enter");
|
||||
// It has not been sent yet
|
||||
await expect(page.locator(".mx_EventTile_body", { hasText: "my message 3" })).not.toBeVisible();
|
||||
|
||||
// Press Control+Enter
|
||||
await composer.press(`${CtrlOrMeta}+Enter`);
|
||||
// It was sent
|
||||
await expect(
|
||||
page.locator(".mx_EventTile_last .mx_EventTile_body", { hasText: "my message 3" }),
|
||||
).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { test, expect } from "../../element-web-test";
|
||||
import { SettingLevel } from "../../../src/settings/SettingLevel";
|
||||
|
||||
const CtrlOrMeta = process.platform === "darwin" ? "Meta" : "Control";
|
||||
|
||||
test.describe("Composer", () => {
|
||||
test.use({
|
||||
displayName: "Janet",
|
||||
});
|
||||
|
||||
test.use({
|
||||
room: async ({ app, user }, use) => {
|
||||
const roomId = await app.client.createRoom({ name: "Composing Room" });
|
||||
await app.viewRoomByName("Composing Room");
|
||||
await use({ roomId });
|
||||
},
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ room }) => {}); // trigger room fixture
|
||||
|
||||
test.describe("Rich text editor", () => {
|
||||
test.use({
|
||||
labsFlags: ["feature_wysiwyg_composer"],
|
||||
});
|
||||
|
||||
test.describe("Commands", () => {
|
||||
// TODO add tests for rich text mode
|
||||
|
||||
test.describe("Plain text mode", () => {
|
||||
test("autocomplete behaviour tests", async ({ page }) => {
|
||||
// Select plain text mode after composer is ready
|
||||
await expect(page.locator("div[contenteditable=true]")).toBeVisible();
|
||||
await page.getByRole("button", { name: "Hide formatting" }).click();
|
||||
|
||||
// Typing a single / displays the autocomplete menu and contents
|
||||
await page.getByRole("textbox").press("/");
|
||||
|
||||
// Check that the autocomplete options are visible and there are more than 0 items
|
||||
await expect(page.getByTestId("autocomplete-wrapper")).not.toBeEmpty();
|
||||
|
||||
// Entering `//` or `/ ` hides the autocomplete contents
|
||||
// Add an extra slash for `//`
|
||||
await page.getByRole("textbox").press("/");
|
||||
await expect(page.getByTestId("autocomplete-wrapper")).toBeEmpty();
|
||||
// Remove the extra slash to go back to `/`
|
||||
await page.getByRole("textbox").press("Backspace");
|
||||
await expect(page.getByTestId("autocomplete-wrapper")).not.toBeEmpty();
|
||||
// Add a trailing space for `/ `
|
||||
await page.getByRole("textbox").press(" ");
|
||||
await expect(page.getByTestId("autocomplete-wrapper")).toBeEmpty();
|
||||
|
||||
// Typing a command that takes no arguments (/devtools) and selecting by click works
|
||||
await page.getByRole("textbox").press("Backspace");
|
||||
await page.getByRole("textbox").pressSequentially("dev");
|
||||
await page.getByTestId("autocomplete-wrapper").getByText("/devtools").click();
|
||||
// Check it has closed the autocomplete and put the text into the composer
|
||||
await expect(page.getByTestId("autocomplete-wrapper")).not.toBeVisible();
|
||||
await expect(page.getByRole("textbox").getByText("/devtools")).toBeVisible();
|
||||
// Send the message and check the devtools dialog appeared, then close it
|
||||
await page.getByRole("button", { name: "Send message" }).click();
|
||||
await expect(page.getByRole("dialog").getByText("Developer Tools")).toBeVisible();
|
||||
await page.getByRole("button", { name: "Close dialog" }).click();
|
||||
|
||||
// Typing a command that takes arguments (/spoiler) and selecting with enter works
|
||||
await page.getByRole("textbox").pressSequentially("/spoil");
|
||||
await expect(page.getByTestId("autocomplete-wrapper").getByText("/spoiler")).toBeVisible();
|
||||
await page.getByRole("textbox").press("Enter");
|
||||
// Check it has closed the autocomplete and put the text into the composer
|
||||
await expect(page.getByTestId("autocomplete-wrapper")).not.toBeVisible();
|
||||
await expect(page.getByRole("textbox").getByText("/spoiler")).toBeVisible();
|
||||
// Enter some more text, then send the message
|
||||
await page.getByRole("textbox").pressSequentially("this is the spoiler text ");
|
||||
await page.getByRole("button", { name: "Send message" }).click();
|
||||
// Check that a spoiler item has appeared in the timeline and locator the spoiler command text
|
||||
await expect(page.locator("button.mx_EventTile_spoiler")).toBeVisible();
|
||||
await expect(page.getByText("this is the spoiler text")).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Mentions", () => {
|
||||
// TODO add tests for rich text mode
|
||||
|
||||
test.describe("Plain text mode", () => {
|
||||
test.use({
|
||||
botCreateOpts: {
|
||||
displayName: "Bob",
|
||||
},
|
||||
});
|
||||
|
||||
// https://github.com/vector-im/element-web/issues/26037
|
||||
test.skip("autocomplete behaviour tests", async ({ page, app, bot: bob }) => {
|
||||
// Set up a private room so we have another user to mention
|
||||
await app.client.createRoom({
|
||||
is_direct: true,
|
||||
invite: [bob.credentials.userId],
|
||||
});
|
||||
await app.viewRoomByName("Bob");
|
||||
|
||||
// Select plain text mode after composer is ready
|
||||
await expect(page.locator("div[contenteditable=true]")).toBeVisible();
|
||||
await page.getByRole("button", { name: "Hide formatting" }).click();
|
||||
|
||||
// Typing a single @ does not display the autocomplete menu and contents
|
||||
await page.getByRole("textbox").press("@");
|
||||
await expect(page.getByTestId("autocomplete-wrapper")).toBeEmpty();
|
||||
|
||||
// Entering the first letter of the other user's name opens the autocomplete...
|
||||
await page.getByRole("textbox").pressSequentially(bob.credentials.displayName.slice(0, 1));
|
||||
// ...with the other user name visible, and clicking that username...
|
||||
await page.getByTestId("autocomplete-wrapper").getByText(bob.credentials.displayName).click();
|
||||
// ...inserts the username into the composer
|
||||
const pill = page.getByRole("textbox").getByText(bob.credentials.displayName, { exact: false });
|
||||
await expect(pill).toHaveAttribute("contenteditable", "false");
|
||||
await expect(pill).toHaveAttribute("data-mention-type", "user");
|
||||
|
||||
// Send the message to clear the composer
|
||||
await page.getByRole("button", { name: "Send message" }).click();
|
||||
|
||||
// Typing an @, then other user's name, then trailing space closes the autocomplete
|
||||
await page.getByRole("textbox").pressSequentially(`@${bob.credentials.displayName} `);
|
||||
await expect(page.getByTestId("autocomplete-wrapper")).toBeEmpty();
|
||||
|
||||
// Send the message to clear the composer
|
||||
await page.getByRole("button", { name: "Send message" }).click();
|
||||
|
||||
// Moving the cursor back to an "incomplete" mention opens the autocomplete
|
||||
await page
|
||||
.getByRole("textbox")
|
||||
.pressSequentially(`initial text @${bob.credentials.displayName.slice(0, 1)} abc`);
|
||||
await expect(page.getByTestId("autocomplete-wrapper")).toBeEmpty();
|
||||
// Move the cursor left by 4 to put it to: `@B| abc`, check autocomplete displays
|
||||
await page.getByRole("textbox").press("LeftArrow");
|
||||
await page.getByRole("textbox").press("LeftArrow");
|
||||
await page.getByRole("textbox").press("LeftArrow");
|
||||
await page.getByRole("textbox").press("LeftArrow");
|
||||
await expect(page.getByTestId("autocomplete-wrapper")).not.toBeEmpty();
|
||||
|
||||
// Selecting the autocomplete option using Enter inserts it into the composer
|
||||
await page.getByRole("textbox").press("Enter");
|
||||
const pill2 = page.getByRole("textbox").getByText(bob.credentials.displayName, { exact: false });
|
||||
await expect(pill2).toHaveAttribute("contenteditable", "false");
|
||||
await expect(pill2).toHaveAttribute("data-mention-type", "user");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test("sends a message when you click send or press Enter", async ({ page }) => {
|
||||
// Type a message
|
||||
await page.locator("div[contenteditable=true]").pressSequentially("my message 0");
|
||||
// It has not been sent yet
|
||||
await expect(page.locator(".mx_EventTile_body", { hasText: "my message 0" })).not.toBeVisible();
|
||||
|
||||
// Click send
|
||||
await page.getByRole("button", { name: "Send message" }).click();
|
||||
// It has been sent
|
||||
await expect(page.locator(".mx_EventTile_last .mx_EventTile_body").getByText("my message 0")).toBeVisible();
|
||||
|
||||
// Type another
|
||||
await page.locator("div[contenteditable=true]").pressSequentially("my message 1");
|
||||
// Send message
|
||||
page.locator("div[contenteditable=true]").press("Enter");
|
||||
// It was sent
|
||||
await expect(page.locator(".mx_EventTile_last .mx_EventTile_body").getByText("my message 1")).toBeVisible();
|
||||
});
|
||||
|
||||
test("sends only one message when you press Enter multiple times", async ({ page }) => {
|
||||
// Type a message
|
||||
await page.locator("div[contenteditable=true]").pressSequentially("my message 0");
|
||||
// It has not been sent yet
|
||||
await expect(page.locator(".mx_EventTile_body", { hasText: "my message 0" })).not.toBeVisible();
|
||||
|
||||
// Click send
|
||||
await page.locator("div[contenteditable=true]").press("Enter");
|
||||
await page.locator("div[contenteditable=true]").press("Enter");
|
||||
await page.locator("div[contenteditable=true]").press("Enter");
|
||||
// It has been sent
|
||||
await expect(page.locator(".mx_EventTile_last .mx_EventTile_body").getByText("my message 0")).toBeVisible();
|
||||
await expect(page.locator(".mx_EventTile_last .mx_EventTile_body")).toHaveCount(1);
|
||||
});
|
||||
|
||||
test("can write formatted text", async ({ page }) => {
|
||||
await page.locator("div[contenteditable=true]").pressSequentially("my ");
|
||||
await page.locator("div[contenteditable=true]").press(`${CtrlOrMeta}+KeyB`);
|
||||
await page.locator("div[contenteditable=true]").pressSequentially("bold");
|
||||
await page.locator("div[contenteditable=true]").press(`${CtrlOrMeta}+KeyB`);
|
||||
await page.locator("div[contenteditable=true]").pressSequentially(" message");
|
||||
await page.getByRole("button", { name: "Send message" }).click();
|
||||
await expect(page.locator(".mx_EventTile_body strong").getByText("bold")).toBeVisible();
|
||||
});
|
||||
|
||||
test.describe("when Control+Enter is required to send", () => {
|
||||
test.beforeEach(async ({ app }) => {
|
||||
await app.settings.setValue("MessageComposerInput.ctrlEnterToSend", null, SettingLevel.ACCOUNT, true);
|
||||
});
|
||||
|
||||
test("only sends when you press Control+Enter", async ({ page }) => {
|
||||
// Type a message and press Enter
|
||||
await page.locator("div[contenteditable=true]").pressSequentially("my message 3");
|
||||
await page.locator("div[contenteditable=true]").press("Enter");
|
||||
// It has not been sent yet
|
||||
await expect(page.locator(".mx_EventTile_body", { hasText: "my message 3" })).not.toBeVisible();
|
||||
|
||||
// Press Control+Enter
|
||||
await page.locator("div[contenteditable=true]").press("Control+Enter");
|
||||
// It was sent
|
||||
await expect(
|
||||
page.locator(".mx_EventTile_last .mx_EventTile_body").getByText("my message 3"),
|
||||
).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("links", () => {
|
||||
test("create link with a forward selection", async ({ page }) => {
|
||||
// Type a message
|
||||
await page.locator("div[contenteditable=true]").pressSequentially("my message 0");
|
||||
await page.locator("div[contenteditable=true]").press(`${CtrlOrMeta}+A`);
|
||||
|
||||
// Open link modal
|
||||
await page.getByRole("button", { name: "Link" }).click();
|
||||
// Fill the link field
|
||||
await page.getByRole("textbox", { name: "Link" }).pressSequentially("https://matrix.org/");
|
||||
// Click on save
|
||||
await page.getByRole("button", { name: "Save" }).click();
|
||||
// Send the message
|
||||
await page.getByRole("button", { name: "Send message" }).click();
|
||||
|
||||
// It was sent
|
||||
await expect(page.locator(".mx_EventTile_body a").getByText("my message 0")).toBeVisible();
|
||||
await expect(page.locator(".mx_EventTile_body a")).toHaveAttribute(
|
||||
"href",
|
||||
new RegExp("https://matrix.org/"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Drafts", () => {
|
||||
test("drafts with rich and plain text", async ({ page, app }) => {
|
||||
// Set up a second room to swtich to, to test drafts
|
||||
const firstRoomname = "Composing Room";
|
||||
const secondRoomname = "Second Composing Room";
|
||||
await app.client.createRoom({ name: secondRoomname });
|
||||
|
||||
// Composer is visible
|
||||
const composer = page.locator("div[contenteditable=true]");
|
||||
await expect(composer).toBeVisible();
|
||||
|
||||
// Type some formatted text
|
||||
await composer.pressSequentially("my ");
|
||||
await composer.press(`${CtrlOrMeta}+KeyB`);
|
||||
await composer.pressSequentially("bold");
|
||||
|
||||
// Change to plain text mode
|
||||
await page.getByRole("button", { name: "Hide formatting" }).click();
|
||||
|
||||
// Change to another room and back again
|
||||
await app.viewRoomByName(secondRoomname);
|
||||
await app.viewRoomByName(firstRoomname);
|
||||
|
||||
// assert the markdown
|
||||
await expect(page.locator("div[contenteditable=true]", { hasText: "my __bold__" })).toBeVisible();
|
||||
|
||||
// Change to plain text mode and assert the markdown
|
||||
await page.getByRole("button", { name: "Show formatting" }).click();
|
||||
|
||||
// Change to another room and back again
|
||||
await app.viewRoomByName(secondRoomname);
|
||||
await app.viewRoomByName(firstRoomname);
|
||||
|
||||
// Send the message and assert the message
|
||||
await page.getByRole("button", { name: "Send message" }).click();
|
||||
await expect(page.locator(".mx_EventTile_last .mx_EventTile_body").getByText("my bold")).toBeVisible();
|
||||
});
|
||||
|
||||
test("draft with replies", async ({ page, app }) => {
|
||||
// Set up a second room to swtich to, to test drafts
|
||||
const firstRoomname = "Composing Room";
|
||||
const secondRoomname = "Second Composing Room";
|
||||
await app.client.createRoom({ name: secondRoomname });
|
||||
|
||||
// Composer is visible
|
||||
const composer = page.locator("div[contenteditable=true]");
|
||||
await expect(composer).toBeVisible();
|
||||
|
||||
// Send a message
|
||||
await composer.pressSequentially("my first message");
|
||||
await page.getByRole("button", { name: "Send message" }).click();
|
||||
|
||||
// Click reply
|
||||
const tile = page.locator(".mx_EventTile_last");
|
||||
await tile.hover();
|
||||
await tile.getByRole("button", { name: "Reply", exact: true }).click();
|
||||
|
||||
// Type reply text
|
||||
await composer.pressSequentially("my reply");
|
||||
|
||||
// Change to another room and back again
|
||||
await app.viewRoomByName(secondRoomname);
|
||||
await app.viewRoomByName(firstRoomname);
|
||||
|
||||
// Assert reply mode and reply text
|
||||
await expect(page.getByText("Replying")).toBeVisible();
|
||||
await expect(page.locator("div[contenteditable=true]", { hasText: "my reply" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("draft in threads", async ({ page, app }) => {
|
||||
// Set up a second room to swtich to, to test drafts
|
||||
const firstRoomname = "Composing Room";
|
||||
const secondRoomname = "Second Composing Room";
|
||||
await app.client.createRoom({ name: secondRoomname });
|
||||
|
||||
// Composer is visible
|
||||
const composer = page.locator("div[contenteditable=true]");
|
||||
await expect(composer).toBeVisible();
|
||||
|
||||
// Send a message
|
||||
await composer.pressSequentially("my first message");
|
||||
await page.getByRole("button", { name: "Send message" }).click();
|
||||
|
||||
// Click reply
|
||||
const tile = page.locator(".mx_EventTile_last");
|
||||
await tile.hover();
|
||||
await tile.getByRole("button", { name: "Reply in thread" }).click();
|
||||
|
||||
const thread = page.locator(".mx_ThreadView");
|
||||
const threadComposer = thread.locator("div[contenteditable=true]");
|
||||
|
||||
// Type threaded text
|
||||
await threadComposer.pressSequentially("my threaded message");
|
||||
|
||||
// Change to another room and back again
|
||||
await app.viewRoomByName(secondRoomname);
|
||||
await app.viewRoomByName(firstRoomname);
|
||||
|
||||
// Assert threaded draft
|
||||
await expect(
|
||||
thread.locator("div[contenteditable=true]", { hasText: "my threaded message" }),
|
||||
).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user