mirror of
https://github.com/vector-im/element-web.git
synced 2025-11-09 20:51:41 +01:00
* Show timestamps when keyboard focus is within an event tile Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Ensure toolbar navigation pattern works in MessageActionBar This requires all buttons within to be roving by using the ref callback given by useRovingTabIndex Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Use PureComponent in EventTile to avoid mass re-rendering due to transitive onFocus/onBlur calls Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Remove unused timestamp event tile prop Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Use MessageTimestamp to generate the wrapping anchor so that focusing it brings up the tooltip Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Tweak MessageTimestamp Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Switch back to Component as we specify a shouldComponentUpdate already Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update jest tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update playwright timestamp masks Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Revert snapshot Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update snapshot Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix IRC layout Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Use PureComponent in EventTile to avoid mass re-rendering due to transitive onFocus/onBlur calls Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Remove unused timestamp event tile prop Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Use MessageTimestamp to generate the wrapping anchor so that focusing it brings up the tooltip Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Tweak MessageTimestamp Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Switch back to Component as we specify a shouldComponentUpdate already Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update jest tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update playwright timestamp masks Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Revert snapshot Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update snapshot Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix IRC layout Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Lint styles Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix layout picker Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update snapshots Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update screenshots Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix pcss comment Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Playwright Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Playwright Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
163 lines
6.7 KiB
TypeScript
163 lines
6.7 KiB
TypeScript
/*
|
|
Copyright 2025 New Vector Ltd.
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
Please see LICENSE files in the repository root for full details.
|
|
*/
|
|
|
|
import { type Page } from "@playwright/test";
|
|
import fs from "node:fs";
|
|
|
|
import { test, expect } from "../../element-web-test";
|
|
|
|
const screenshotOptions = (page: Page) => ({
|
|
// Hide the jump to bottom button in the timeline to avoid flakiness
|
|
// Exclude timestamp and read marker from snapshot
|
|
css: `
|
|
.mx_MessageTimestamp {
|
|
visibility: hidden;
|
|
}
|
|
.mx_JumpToBottomButton {
|
|
display: none !important;
|
|
}
|
|
.mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker {
|
|
display: none !important;
|
|
}
|
|
`,
|
|
});
|
|
|
|
const IMAGE_FILE = fs.readFileSync("playwright/sample-files/element.png");
|
|
|
|
test.describe("Custom Component API", () => {
|
|
test.use({
|
|
displayName: "Manny",
|
|
config: {
|
|
modules: ["/modules/custom-component-module.js"],
|
|
},
|
|
page: async ({ page }, use) => {
|
|
await page.route("/modules/custom-component-module.js", async (route) => {
|
|
await route.fulfill({ path: "playwright/sample-files/custom-component-module.js" });
|
|
});
|
|
await use(page);
|
|
},
|
|
room: async ({ page, app, user, bot }, use) => {
|
|
const roomId = await app.client.createRoom({ name: "TestRoom" });
|
|
await use({ roomId });
|
|
},
|
|
});
|
|
test.describe("basic functionality", () => {
|
|
test(
|
|
"should replace the render method of a textual event",
|
|
{ tag: "@screenshot" },
|
|
async ({ page, room, app }) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await app.client.sendMessage(room.roomId, "Simple message");
|
|
await expect(await page.locator(".mx_EventTile_last")).toMatchScreenshot(
|
|
"custom-component-tile.png",
|
|
screenshotOptions(page),
|
|
);
|
|
},
|
|
);
|
|
test(
|
|
"should fall through if one module does not render a component",
|
|
{ tag: "@screenshot" },
|
|
async ({ page, room, app }) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await app.client.sendMessage(room.roomId, "Fall through here");
|
|
await expect(await page.locator(".mx_EventTile_last")).toMatchScreenshot(
|
|
"custom-component-tile-fall-through.png",
|
|
screenshotOptions(page),
|
|
);
|
|
},
|
|
);
|
|
test(
|
|
"should render the original content of a textual event conditionally",
|
|
{ tag: "@screenshot" },
|
|
async ({ page, room, app }) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await app.client.sendMessage(room.roomId, "Do not replace me");
|
|
await expect(await page.locator(".mx_EventTile_last")).toMatchScreenshot(
|
|
"custom-component-tile-original.png",
|
|
screenshotOptions(page),
|
|
);
|
|
},
|
|
);
|
|
test("should disallow editing when the allowEditingEvent hint is set to false", async ({ page, room, app }) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await app.client.sendMessage(room.roomId, "Do not show edits");
|
|
await page.getByText("Do not show edits").hover();
|
|
await expect(
|
|
await page.getByRole("toolbar", { name: "Message Actions" }).getByRole("button", { name: "Edit" }),
|
|
).not.toBeVisible();
|
|
});
|
|
test("should disallow downloading media when the allowDownloading hint is set to false", async ({
|
|
page,
|
|
room,
|
|
app,
|
|
}) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await app.viewRoomById(room.roomId);
|
|
const upload = await app.client.uploadContent(IMAGE_FILE, { name: "bad.png", type: "image/png" });
|
|
await app.client.sendEvent(room.roomId, null, "m.room.message", {
|
|
msgtype: "m.image",
|
|
body: "bad.png",
|
|
url: upload.content_uri,
|
|
});
|
|
|
|
await app.timeline.scrollToBottom();
|
|
const imgTile = page.locator(".mx_MImageBody").first();
|
|
await expect(imgTile).toBeVisible();
|
|
await imgTile.hover();
|
|
await expect(page.getByRole("button", { name: "Download" })).not.toBeVisible();
|
|
await imgTile.click();
|
|
await expect(page.getByLabel("Image view").getByLabel("Download")).not.toBeVisible();
|
|
});
|
|
test("should allow downloading media when the allowDownloading hint is set to true", async ({
|
|
page,
|
|
room,
|
|
app,
|
|
}) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await app.viewRoomById(room.roomId);
|
|
const upload = await app.client.uploadContent(IMAGE_FILE, { name: "good.png", type: "image/png" });
|
|
await app.client.sendEvent(room.roomId, null, "m.room.message", {
|
|
msgtype: "m.image",
|
|
body: "good.png",
|
|
url: upload.content_uri,
|
|
});
|
|
|
|
await app.timeline.scrollToBottom();
|
|
const imgTile = page.locator(".mx_MImageBody").first();
|
|
await expect(imgTile).toBeVisible();
|
|
await imgTile.hover();
|
|
await expect(page.getByRole("button", { name: "Download" })).toBeVisible();
|
|
await imgTile.click();
|
|
await expect(page.getByLabel("Image view").getByLabel("Download")).toBeVisible();
|
|
});
|
|
test(
|
|
"should render the next registered component if the filter function throws",
|
|
{ tag: "@screenshot" },
|
|
async ({ page, room, app }) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await app.client.sendMessage(room.roomId, "Crash the filter!");
|
|
await expect(await page.locator(".mx_EventTile_last")).toMatchScreenshot(
|
|
"custom-component-crash-handle-filter.png",
|
|
screenshotOptions(page),
|
|
);
|
|
},
|
|
);
|
|
test(
|
|
"should render original component if the render function throws",
|
|
{ tag: "@screenshot" },
|
|
async ({ page, room, app }) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await app.client.sendMessage(room.roomId, "Crash the renderer!");
|
|
await expect(await page.locator(".mx_EventTile_last")).toMatchScreenshot(
|
|
"custom-component-crash-handle-renderer.png",
|
|
screenshotOptions(page),
|
|
);
|
|
},
|
|
);
|
|
});
|
|
});
|