diff --git a/cypress/e2e/room/room-header.spec.ts b/cypress/e2e/room/room-header.spec.ts deleted file mode 100644 index d92d505167..0000000000 --- a/cypress/e2e/room/room-header.spec.ts +++ /dev/null @@ -1,286 +0,0 @@ -/* -Copyright 2023 Suguru Hirahara - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/// - -import { IWidget } from "matrix-widget-api"; - -import { HomeserverInstance } from "../../plugins/utils/homeserver"; -import { SettingLevel } from "../../../src/settings/SettingLevel"; - -describe("Room Header", () => { - let homeserver: HomeserverInstance; - - beforeEach(() => { - cy.startHomeserver("default").then((data) => { - homeserver = data; - cy.initTestUser(homeserver, "Sakura"); - }); - }); - - afterEach(() => { - cy.stopHomeserver(homeserver); - }); - - it("should render default buttons properly", () => { - cy.enableLabsFeature("feature_notifications"); - cy.createRoom({ name: "Test Room" }).viewRoomByName("Test Room"); - - cy.get(".mx_LegacyRoomHeader").within(() => { - // Names (aria-label) of every button rendered on mx_LegacyRoomHeader by default - const expectedButtonNames = [ - "Room options", // The room name button next to the room avatar, which renders dropdown menu on click - "Voice call", - "Video call", - "Search", - "Threads", - "Notifications", - "Room info", - ]; - - // Assert they are found and visible - for (const name of expectedButtonNames) { - cy.findByRole("button", { name }).should("be.visible"); - } - - // Assert that just those seven buttons exist on mx_LegacyRoomHeader by default - cy.findAllByRole("button").should("have.length", 7); - }); - - cy.get(".mx_LegacyRoomHeader").percySnapshotElement("Room header"); - }); - - it("should render the pin button for pinned messages card", () => { - cy.enableLabsFeature("feature_pinning"); - - cy.createRoom({ name: "Test Room" }).viewRoomByName("Test Room"); - - cy.getComposer().type("Test message{enter}"); - - cy.get(".mx_EventTile_last").realHover().findByRole("button", { name: "Options" }).click(); - - cy.findByRole("menuitem", { name: "Pin" }).should("be.visible").click(); - - cy.get(".mx_LegacyRoomHeader").within(() => { - cy.findByRole("button", { name: "Pinned messages" }).should("be.visible"); - }); - }); - - it("should render a very long room name without collapsing the buttons", () => { - cy.enableLabsFeature("feature_notifications"); - const LONG_ROOM_NAME = - "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore " + - "et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " + - "aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum " + - "dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui " + - "officia deserunt mollit anim id est laborum."; - - cy.createRoom({ name: LONG_ROOM_NAME }).viewRoomByName(LONG_ROOM_NAME); - - cy.get(".mx_LegacyRoomHeader").within(() => { - // Wait until the room name is set - cy.get(".mx_LegacyRoomHeader_nametext").within(() => { - cy.findByText(LONG_ROOM_NAME).should("exist"); - }); - - // Assert the size of buttons on RoomHeader are specified and the buttons are not compressed - // Note these assertions do not check the size of mx_LegacyRoomHeader_name button - cy.get(".mx_LegacyRoomHeader_button") - .should("have.length", 6) - .should("be.visible") - .should("have.css", "height", "32px") - .should("have.css", "width", "32px"); - }); - - cy.get(".mx_LegacyRoomHeader").percySnapshotElement("Room header - with a long room name", { - widths: [300, 600], // Magic numbers to emulate the narrow RoomHeader on the actual UI - }); - }); - - it("should have buttons highlighted by being clicked", () => { - cy.enableLabsFeature("feature_notifications"); - cy.createRoom({ name: "Test Room" }).viewRoomByName("Test Room"); - - cy.get(".mx_LegacyRoomHeader").within(() => { - // Check these buttons - const buttonsHighlighted = ["Threads", "Notifications", "Room info"]; - - for (const name of buttonsHighlighted) { - cy.findByRole("button", { name: name }).click(); // Highlight the button - } - }); - - cy.get(".mx_LegacyRoomHeader").percySnapshotElement("Room header - with a highlighted button"); - }); - - describe("with a video room", () => { - const createVideoRoom = () => { - // Enable video rooms. This command reloads the app - cy.setSettingValue("feature_video_rooms", null, SettingLevel.DEVICE, true); - - cy.get(".mx_LeftPanel_roomListContainer", { timeout: 20000 }) - .findByRole("button", { name: "Add room" }) - .click(); - - cy.findByRole("menuitem", { name: "New video room" }).click(); - - cy.findByRole("textbox", { name: "Name" }).type("Test video room"); - - cy.findByRole("button", { name: "Create video room" }).click(); - - cy.viewRoomByName("Test video room"); - }; - - it("should render buttons for room options, beta pill, invite, chat, and room info", () => { - cy.enableLabsFeature("feature_notifications"); - createVideoRoom(); - - cy.get(".mx_LegacyRoomHeader").within(() => { - // Names (aria-label) of the buttons on the video room header - const expectedButtonNames = [ - "Room options", - "Video rooms are a beta feature Click for more info", // Beta pill - "Invite", - "Chat", - "Room info", - ]; - - // Assert they are found and visible - for (const name of expectedButtonNames) { - cy.findByRole("button", { name }).should("be.visible"); - } - - // Assert that there is not a button except those buttons - cy.findAllByRole("button").should("have.length", 7); - }); - - cy.get(".mx_LegacyRoomHeader").percySnapshotElement("Room header - with a video room"); - }); - - it("should render a working chat button which opens the timeline on a right panel", () => { - createVideoRoom(); - - cy.get(".mx_LegacyRoomHeader").findByRole("button", { name: "Chat" }).click(); - - // Assert that the video is rendered - cy.get(".mx_CallView video").should("exist"); - - cy.get(".mx_RightPanel .mx_TimelineCard") - .should("exist") - .within(() => { - // Assert that GELS is visible - cy.findByText("Sakura created and configured the room.").should("exist"); - }); - }); - }); - - describe("with a widget", () => { - const ROOM_NAME = "Test Room with a widget"; - const WIDGET_ID = "fake-widget"; - const WIDGET_HTML = ` - - - Fake Widget - - - Hello World - - - `; - - let widgetUrl: string; - let roomId: string; - - beforeEach(() => { - cy.serveHtmlFile(WIDGET_HTML).then((url) => { - widgetUrl = url; - }); - - cy.createRoom({ name: ROOM_NAME }).then((id) => { - roomId = id; - - // setup widget via state event - cy.getClient() - .then(async (matrixClient) => { - const content: IWidget = { - id: WIDGET_ID, - creatorUserId: "somebody", - type: "widget", - name: "widget", - url: widgetUrl, - }; - await matrixClient.sendStateEvent(roomId, "im.vector.modular.widgets", content, WIDGET_ID); - }) - .as("widgetEventSent"); - - // set initial layout - cy.getClient() - .then(async (matrixClient) => { - const content = { - widgets: { - [WIDGET_ID]: { - container: "top", - index: 1, - width: 100, - height: 0, - }, - }, - }; - await matrixClient.sendStateEvent(roomId, "io.element.widgets.layout", content, ""); - }) - .as("layoutEventSent"); - }); - - cy.all([cy.get("@widgetEventSent"), cy.get("@layoutEventSent")]).then(() => { - // open the room - cy.viewRoomByName(ROOM_NAME); - }); - }); - - it("should highlight the apps button", () => { - // Assert that AppsDrawer is rendered - cy.get(".mx_AppsDrawer").should("exist"); - - cy.get(".mx_LegacyRoomHeader").within(() => { - // Assert that "Hide Widgets" button is rendered and aria-checked is set to true - cy.findByRole("button", { name: "Hide Widgets" }) - .should("exist") - .should("have.attr", "aria-checked", "true"); - }); - - cy.get(".mx_LegacyRoomHeader").percySnapshotElement("Room header - with apps button (highlighted)"); - }); - - it("should support hiding a widget", () => { - cy.get(".mx_AppsDrawer").should("exist"); - - cy.get(".mx_LegacyRoomHeader").within(() => { - // Click the apps button to hide AppsDrawer - cy.findByRole("button", { name: "Hide Widgets" }).should("exist").click(); - - // Assert that "Show widgets" button is rendered and aria-checked is set to false - cy.findByRole("button", { name: "Show Widgets" }) - .should("exist") - .should("have.attr", "aria-checked", "false"); - }); - - // Assert that AppsDrawer is not rendered - cy.get(".mx_AppsDrawer").should("not.exist"); - - cy.get(".mx_LegacyRoomHeader").percySnapshotElement("Room header - with apps button (not highlighted)"); - }); - }); -}); diff --git a/cypress/e2e/room/room.spec.ts b/cypress/e2e/room/room.spec.ts deleted file mode 100644 index 8d1108d280..0000000000 --- a/cypress/e2e/room/room.spec.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/// - -import { EventType } from "matrix-js-sdk/src/matrix"; - -import { HomeserverInstance } from "../../plugins/utils/homeserver"; -import { MatrixClient } from "../../global"; - -describe("Room Directory", () => { - let homeserver: HomeserverInstance; - - beforeEach(() => { - cy.startHomeserver("default").then((data) => { - homeserver = data; - - cy.initTestUser(homeserver, "Alice"); - }); - }); - - afterEach(() => { - cy.stopHomeserver(homeserver); - }); - - it("should switch between existing dm rooms without a loader", () => { - let bobClient: MatrixClient; - let charlieClient: MatrixClient; - cy.getBot(homeserver, { - displayName: "Bob", - }).then((bob) => { - bobClient = bob; - }); - - cy.getBot(homeserver, { - displayName: "Charlie", - }).then((charlie) => { - charlieClient = charlie; - }); - - // create dms with bob and charlie - cy.getClient().then(async (cli) => { - const bobRoom = await cli.createRoom({ is_direct: true }); - const charlieRoom = await cli.createRoom({ is_direct: true }); - await cli.invite(bobRoom.room_id, bobClient.getUserId()); - await cli.invite(charlieRoom.room_id, charlieClient.getUserId()); - await cli.setAccountData("m.direct" as EventType, { - [bobClient.getUserId()]: [bobRoom.room_id], - [charlieClient.getUserId()]: [charlieRoom.room_id], - }); - }); - - cy.wait(250); // let the room list settle - - cy.viewRoomByName("Bob"); - - // short timeout because loader is only visible for short period - // we want to make sure it is never displayed when switching these rooms - cy.get(".mx_RoomPreviewBar_spinnerTitle", { timeout: 1 }).should("not.exist"); - // confirm the room was loaded - cy.findByText("Bob joined the room").should("exist"); - - cy.viewRoomByName("Charlie"); - cy.get(".mx_RoomPreviewBar_spinnerTitle", { timeout: 1 }).should("not.exist"); - // confirm the room was loaded - cy.findByText("Charlie joined the room").should("exist"); - }); -}); diff --git a/playwright/e2e/room/room-header.spec.ts b/playwright/e2e/room/room-header.spec.ts new file mode 100644 index 0000000000..45bb6a6810 --- /dev/null +++ b/playwright/e2e/room/room-header.spec.ts @@ -0,0 +1,280 @@ +/* +Copyright 2023 Suguru Hirahara + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { Page } from "@playwright/test"; + +import { test, expect } from "../../element-web-test"; +import { ElementAppPage } from "../../pages/ElementAppPage"; + +test.describe("Room Header", () => { + test.use({ + displayName: "Sakura", + }); + + test.describe("with feature_notifications enabled", () => { + test.beforeEach(async ({ app }) => { + await app.labs.enableLabsFeature("feature_notifications"); + }); + + test("should render default buttons properly", async ({ page, app, user }) => { + await app.client.createRoom({ name: "Test Room" }); + await app.viewRoomByName("Test Room"); + + const header = page.locator(".mx_LegacyRoomHeader"); + // Names (aria-label) of every button rendered on mx_LegacyRoomHeader by default + const expectedButtonNames = [ + "Room options", // The room name button next to the room avatar, which renders dropdown menu on click + "Voice call", + "Video call", + "Search", + "Threads", + "Notifications", + "Room info", + ]; + + // Assert they are found and visible + for (const name of expectedButtonNames) { + await expect(header.getByRole("button", { name })).toBeVisible(); + } + + // Assert that just those seven buttons exist on mx_LegacyRoomHeader by default + await expect(header.getByRole("button")).toHaveCount(7); + + await expect(header).toMatchScreenshot("room-header.png"); + }); + + test("should render a very long room name without collapsing the buttons", async ({ page, app, user }) => { + const LONG_ROOM_NAME = + "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore " + + "et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " + + "aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum " + + "dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui " + + "officia deserunt mollit anim id est laborum."; + + await app.client.createRoom({ name: LONG_ROOM_NAME }); + await app.viewRoomByName(LONG_ROOM_NAME); + + const header = page.locator(".mx_LegacyRoomHeader"); + // Wait until the room name is set + await expect(page.locator(".mx_LegacyRoomHeader_nametext").getByText(LONG_ROOM_NAME)).toBeVisible(); + + // Assert the size of buttons on RoomHeader are specified and the buttons are not compressed + // Note these assertions do not check the size of mx_LegacyRoomHeader_name button + const buttons = page.locator(".mx_LegacyRoomHeader_button"); + await expect(buttons).toHaveCount(6); + for (const button of await buttons.all()) { + await expect(button).toBeVisible(); + await expect(button).toHaveCSS("height", "32px"); + await expect(button).toHaveCSS("width", "32px"); + } + + await expect(header).toMatchScreenshot("room-header-long-name.png"); + }); + + test("should have buttons highlighted by being clicked", async ({ page, app, user }) => { + await app.client.createRoom({ name: "Test Room" }); + await app.viewRoomByName("Test Room"); + + const header = page.locator(".mx_LegacyRoomHeader"); + // Check these buttons + const buttonsHighlighted = ["Threads", "Notifications", "Room info"]; + + for (const name of buttonsHighlighted) { + await header.getByRole("button", { name: name }).click(); // Highlight the button + } + + await expect(header).toMatchScreenshot("room-header-highlighted.png"); + }); + }); + + test.describe("with feature_pinning enabled", () => { + test.beforeEach(async ({ app }) => { + await app.labs.enableLabsFeature("feature_pinning"); + }); + + test("should render the pin button for pinned messages card", async ({ page, app, user }) => { + await app.client.createRoom({ name: "Test Room" }); + await app.viewRoomByName("Test Room"); + + const composer = app.getComposer().locator("[contenteditable]"); + await composer.fill("Test message"); + await composer.press("Enter"); + + const lastTile = page.locator(".mx_EventTile_last"); + await lastTile.hover(); + await lastTile.getByRole("button", { name: "Options" }).click(); + + await page.getByRole("menuitem", { name: "Pin" }).click(); + + await expect( + page.locator(".mx_LegacyRoomHeader").getByRole("button", { name: "Pinned messages" }), + ).toBeVisible(); + }); + }); + + test.describe("with a video room", () => { + test.beforeEach(async ({ app }) => { + await app.labs.enableLabsFeature("feature_video_rooms"); + }); + + const createVideoRoom = async (page: Page, app: ElementAppPage) => { + await page.locator(".mx_LeftPanel_roomListContainer").getByRole("button", { name: "Add room" }).click(); + + await page.getByRole("menuitem", { name: "New video room" }).click(); + + await page.getByRole("textbox", { name: "Name" }).type("Test video room"); + + await page.getByRole("button", { name: "Create video room" }).click(); + + await app.viewRoomByName("Test video room"); + }; + + test("should render buttons for room options, beta pill, invite, chat, and room info", async ({ + page, + app, + user, + }) => { + await app.labs.enableLabsFeature("feature_notifications"); + await createVideoRoom(page, app); + + const header = page.locator(".mx_LegacyRoomHeader"); + // Names (aria-label) of the buttons on the video room header + const expectedButtonNames = [ + "Room options", + "Video rooms are a beta feature Click for more info", // Beta pill + "Invite", + "Chat", + "Room info", + ]; + + // Assert they are found and visible + for (const name of expectedButtonNames) { + await expect(header.getByRole("button", { name })).toBeVisible(); + } + + // Assert that there is not a button except those buttons + await expect(header.getByRole("button")).toHaveCount(7); + + await expect(header).toMatchScreenshot("room-header-video-room.png"); + }); + + test("should render a working chat button which opens the timeline on a right panel", async ({ + page, + app, + user, + }) => { + await createVideoRoom(page, app); + + await page.locator(".mx_LegacyRoomHeader").getByRole("button", { name: "Chat" }).click(); + + // Assert that the video is rendered + await expect(page.locator(".mx_CallView video")).toBeVisible(); + + // Assert that GELS is visible + await expect( + page.locator(".mx_RightPanel .mx_TimelineCard").getByText("Sakura created and configured the room."), + ).toBeVisible(); + }); + }); + + test.describe("with a widget", () => { + const ROOM_NAME = "Test Room with a widget"; + const WIDGET_ID = "fake-widget"; + const WIDGET_HTML = ` + + + Fake Widget + + + Hello World + + + `; + + test.beforeEach(async ({ page, app, user, webserver }) => { + const widgetUrl = webserver.start(WIDGET_HTML); + const roomId = await app.client.createRoom({ name: ROOM_NAME }); + + // setup widget via state event + await app.client.evaluate( + async (matrixClient, { roomId, widgetUrl, id }) => { + await matrixClient.sendStateEvent( + roomId, + "im.vector.modular.widgets", + { + id, + creatorUserId: "somebody", + type: "widget", + name: "widget", + url: widgetUrl, + }, + id, + ); + await matrixClient.sendStateEvent( + roomId, + "io.element.widgets.layout", + { + widgets: { + [id]: { + container: "top", + index: 1, + width: 100, + height: 0, + }, + }, + }, + "", + ); + }, + { + roomId, + widgetUrl, + id: WIDGET_ID, + }, + ); + + // open the room + await app.viewRoomByName(ROOM_NAME); + }); + + test("should highlight the apps button", async ({ page, app, user }) => { + // Assert that AppsDrawer is rendered + await expect(page.locator(".mx_AppsDrawer")).toBeVisible(); + + const header = page.locator(".mx_LegacyRoomHeader"); + // Assert that "Hide Widgets" button is rendered and aria-checked is set to true + await expect(header.getByRole("button", { name: "Hide Widgets" })).toHaveAttribute("aria-checked", "true"); + + await expect(header).toMatchScreenshot("room-header-with-apps-button-highlighted.png"); + }); + + test("should support hiding a widget", async ({ page, app, user }) => { + await expect(page.locator(".mx_AppsDrawer")).toBeVisible(); + + const header = page.locator(".mx_LegacyRoomHeader"); + // Click the apps button to hide AppsDrawer + await header.getByRole("button", { name: "Hide Widgets" }).click(); + + // Assert that "Show widgets" button is rendered and aria-checked is set to false + await expect(header.getByRole("button", { name: "Show Widgets" })).toHaveAttribute("aria-checked", "false"); + + // Assert that AppsDrawer is not rendered + await expect(page.locator(".mx_AppsDrawer")).not.toBeVisible(); + + await expect(header).toMatchScreenshot("room-header-with-apps-button-not-highlighted.png"); + }); + }); +}); diff --git a/playwright/e2e/room/room.spec.ts b/playwright/e2e/room/room.spec.ts new file mode 100644 index 0000000000..2bd9e06c07 --- /dev/null +++ b/playwright/e2e/room/room.spec.ts @@ -0,0 +1,63 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import type { EventType } from "matrix-js-sdk/src/matrix"; +import { test, expect } from "../../element-web-test"; +import { Bot } from "../../pages/bot"; + +test.describe("Room Directory", () => { + test.use({ + displayName: "Alice", + }); + + test("should switch between existing dm rooms without a loader", async ({ page, homeserver, app, user }) => { + const bob = new Bot(page, homeserver, { displayName: "Bob" }); + await bob.prepareClient(); + const charlie = new Bot(page, homeserver, { displayName: "Charlie" }); + await charlie.prepareClient(); + + // create dms with bob and charlie + await app.client.evaluate( + async (cli, { bob, charlie }) => { + const bobRoom = await cli.createRoom({ is_direct: true }); + const charlieRoom = await cli.createRoom({ is_direct: true }); + await cli.invite(bobRoom.room_id, bob); + await cli.invite(charlieRoom.room_id, charlie); + await cli.setAccountData("m.direct" as EventType, { + [bob]: [bobRoom.room_id], + [charlie]: [charlieRoom.room_id], + }); + }, + { + bob: bob.credentials.userId, + charlie: charlie.credentials.userId, + }, + ); + + await app.viewRoomByName("Bob"); + + // short timeout because loader is only visible for short period + // we want to make sure it is never displayed when switching these rooms + await expect(page.locator(".mx_RoomPreviewBar_spinnerTitle")).not.toBeVisible({ timeout: 1 }); + // confirm the room was loaded + await expect(page.getByText("Bob joined the room")).toBeVisible(); + + await app.viewRoomByName("Charlie"); + await expect(page.locator(".mx_RoomPreviewBar_spinnerTitle")).not.toBeVisible({ timeout: 1 }); + // confirm the room was loaded + await expect(page.getByText("Charlie joined the room")).toBeVisible(); + }); +}); diff --git a/playwright/element-web-test.ts b/playwright/element-web-test.ts index f7c405b3f0..a551ec593d 100644 --- a/playwright/element-web-test.ts +++ b/playwright/element-web-test.ts @@ -29,6 +29,7 @@ import { OAuthServer } from "./plugins/oauth_server"; import { Crypto } from "./pages/crypto"; import { Toasts } from "./pages/toasts"; import { Bot, CreateBotOpts } from "./pages/bot"; +import { Webserver } from "./plugins/webserver"; const CONFIG_JSON: Partial = { // This is deliberately quite a minimal config.json, so that we can test that the default settings @@ -75,6 +76,7 @@ export const test = base.extend< uut?: Locator; // Unit Under Test, useful place to refer a prepared locator botCreateOpts: CreateBotOpts; bot: Bot; + webserver: Webserver; } >({ cryptoBackend: ["legacy", { option: true }], @@ -195,6 +197,13 @@ export const test = base.extend< await bot.prepareClient(); // eagerly register the bot await use(bot); }, + + // eslint-disable-next-line no-empty-pattern + webserver: async ({}, use) => { + const webserver = new Webserver(); + await use(webserver); + webserver.stop(); + }, }); export const expect = baseExpect.extend({ diff --git a/playwright/pages/ElementAppPage.ts b/playwright/pages/ElementAppPage.ts index 6cb6c27e90..0d8a5213fc 100644 --- a/playwright/pages/ElementAppPage.ts +++ b/playwright/pages/ElementAppPage.ts @@ -82,7 +82,7 @@ export class ElementAppPage { * Get the composer element * @param isRightPanel whether to select the right panel composer, otherwise the main timeline composer */ - public async getComposer(isRightPanel?: boolean): Promise { + public getComposer(isRightPanel?: boolean): Locator { const panelClass = isRightPanel ? ".mx_RightPanel" : ".mx_RoomView_body"; return this.page.locator(`${panelClass} .mx_MessageComposer`); } @@ -92,7 +92,7 @@ export class ElementAppPage { * @param isRightPanel whether to select the right panel composer, otherwise the main timeline composer */ public async openMessageComposerOptions(isRightPanel?: boolean): Promise { - const composer = await this.getComposer(isRightPanel); + const composer = this.getComposer(isRightPanel); await composer.getByRole("button", { name: "More options", exact: true }).click(); return this.page.getByRole("menu"); } diff --git a/playwright/pages/labs.ts b/playwright/pages/labs.ts index 397eb08305..55bce18225 100644 --- a/playwright/pages/labs.ts +++ b/playwright/pages/labs.ts @@ -21,12 +21,17 @@ export class Labs { /** * Enables a labs feature for an element session. - * Has to be called before the session is initialized * @param feature labsFeature to enable (e.g. "feature_spotlight") */ public async enableLabsFeature(feature: string): Promise { - await this.page.evaluate((feature) => { - window.localStorage.setItem(`mx_labs_feature_${feature}`, "true"); - }, feature); + if (this.page.url() === "about:blank") { + await this.page.addInitScript((feature) => { + window.localStorage.setItem(`mx_labs_feature_${feature}`, "true"); + }, feature); + } else { + await this.page.evaluate((feature) => { + window.localStorage.setItem(`mx_labs_feature_${feature}`, "true"); + }, feature); + } } } diff --git a/playwright/plugins/webserver/index.ts b/playwright/plugins/webserver/index.ts new file mode 100644 index 0000000000..2fe083f179 --- /dev/null +++ b/playwright/plugins/webserver/index.ts @@ -0,0 +1,46 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import * as http from "http"; +import { AddressInfo } from "net"; + +export class Webserver { + private server?: http.Server; + + public start(html: string): string { + if (this.server) this.stop(); + + this.server = http.createServer((req, res) => { + res.writeHead(200, { + "Content-Type": "text/html", + }); + res.end(html); + }); + this.server.listen(); + + const address = this.server.address() as AddressInfo; + console.log(`Started webserver at ${address.address}:${address.port}`); + return `http://localhost:${address.port}/`; + } + + public stop(): void { + if (!this.server) return; + console.log("Stopping webserver"); + const address = this.server.address() as AddressInfo; + this.server.close(); + console.log(`Stopped webserver at ${address.address}:${address.port}`); + } +} diff --git a/playwright/snapshots/room/room-header.spec.ts/room-header-highlighted-linux.png b/playwright/snapshots/room/room-header.spec.ts/room-header-highlighted-linux.png new file mode 100644 index 0000000000..6d6ea59ae8 Binary files /dev/null and b/playwright/snapshots/room/room-header.spec.ts/room-header-highlighted-linux.png differ diff --git a/playwright/snapshots/room/room-header.spec.ts/room-header-linux.png b/playwright/snapshots/room/room-header.spec.ts/room-header-linux.png new file mode 100644 index 0000000000..c5fd90be57 Binary files /dev/null and b/playwright/snapshots/room/room-header.spec.ts/room-header-linux.png differ diff --git a/playwright/snapshots/room/room-header.spec.ts/room-header-long-name-linux.png b/playwright/snapshots/room/room-header.spec.ts/room-header-long-name-linux.png new file mode 100644 index 0000000000..0cfb88b523 Binary files /dev/null and b/playwright/snapshots/room/room-header.spec.ts/room-header-long-name-linux.png differ diff --git a/playwright/snapshots/room/room-header.spec.ts/room-header-video-room-linux.png b/playwright/snapshots/room/room-header.spec.ts/room-header-video-room-linux.png new file mode 100644 index 0000000000..5d79ae740c Binary files /dev/null and b/playwright/snapshots/room/room-header.spec.ts/room-header-video-room-linux.png differ diff --git a/playwright/snapshots/room/room-header.spec.ts/room-header-with-apps-button-highlighted-linux.png b/playwright/snapshots/room/room-header.spec.ts/room-header-with-apps-button-highlighted-linux.png new file mode 100644 index 0000000000..6016bb7e7a Binary files /dev/null and b/playwright/snapshots/room/room-header.spec.ts/room-header-with-apps-button-highlighted-linux.png differ diff --git a/playwright/snapshots/room/room-header.spec.ts/room-header-with-apps-button-not-highlighted-linux.png b/playwright/snapshots/room/room-header.spec.ts/room-header-with-apps-button-not-highlighted-linux.png new file mode 100644 index 0000000000..ad2bf20e94 Binary files /dev/null and b/playwright/snapshots/room/room-header.spec.ts/room-header-with-apps-button-not-highlighted-linux.png differ