mirror of https://github.com/vector-im/riot-web
Migrate spaces.spec.ts from Cypress to Playwright (#11986)
* Remove old percy media query CSS Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Stabilise soft_logout.spec.ts Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update screenshots using `toMatchScreenshot` assertion with CSS overrides Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix accidentally commented test Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Migrate spaces.spec.ts from Cypress to Playwright Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Add screenshots Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>pull/28217/head
parent
d7c682d05e
commit
eb6101cc1b
|
@ -1,347 +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.
|
||||
*/
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
import type { MatrixClient, Preset, ICreateRoomOpts } from "matrix-js-sdk/src/matrix";
|
||||
import { HomeserverInstance } from "../../plugins/utils/homeserver";
|
||||
import Chainable = Cypress.Chainable;
|
||||
import { UserCredentials } from "../../support/login";
|
||||
|
||||
function openSpaceCreateMenu(): Chainable<JQuery> {
|
||||
cy.findByRole("button", { name: "Create a space" }).click();
|
||||
return cy.get(".mx_SpaceCreateMenu_wrapper .mx_ContextualMenu");
|
||||
}
|
||||
|
||||
function openSpaceContextMenu(spaceName: string): Chainable<JQuery> {
|
||||
cy.getSpacePanelButton(spaceName).rightclick();
|
||||
return cy.get(".mx_SpacePanel_contextMenu");
|
||||
}
|
||||
|
||||
function spaceCreateOptions(spaceName: string, roomIds: string[] = []): ICreateRoomOpts {
|
||||
return {
|
||||
creation_content: {
|
||||
type: "m.space",
|
||||
},
|
||||
initial_state: [
|
||||
{
|
||||
type: "m.room.name",
|
||||
content: {
|
||||
name: spaceName,
|
||||
},
|
||||
},
|
||||
...roomIds.map(spaceChildInitialState),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function spaceChildInitialState(roomId: string): ICreateRoomOpts["initial_state"]["0"] {
|
||||
return {
|
||||
type: "m.space.child",
|
||||
state_key: roomId,
|
||||
content: {
|
||||
via: [roomId.split(":")[1]],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe("Spaces", () => {
|
||||
let homeserver: HomeserverInstance;
|
||||
let user: UserCredentials;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.startHomeserver("default").then((data) => {
|
||||
homeserver = data;
|
||||
|
||||
cy.initTestUser(homeserver, "Sue").then((_user) => {
|
||||
user = _user;
|
||||
cy.mockClipboard();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopHomeserver(homeserver);
|
||||
});
|
||||
|
||||
it("should allow user to create public space", () => {
|
||||
openSpaceCreateMenu();
|
||||
cy.get("#mx_ContextualMenu_Container").percySnapshotElement("Space create menu");
|
||||
cy.get(".mx_SpaceCreateMenu_wrapper .mx_ContextualMenu").within(() => {
|
||||
// Regex pattern due to strings of "mx_SpaceCreateMenuType_public"
|
||||
cy.findByRole("button", { name: /Public/ }).click();
|
||||
|
||||
cy.get('.mx_SpaceBasicSettings_avatarContainer input[type="file"]').selectFile(
|
||||
"cypress/fixtures/riot.png",
|
||||
{ force: true },
|
||||
);
|
||||
cy.findByRole("textbox", { name: "Name" }).type("Let's have a Riot");
|
||||
cy.findByRole("textbox", { name: "Address" }).should("have.value", "lets-have-a-riot");
|
||||
cy.findByRole("textbox", { name: "Description" }).type("This is a space to reminisce Riot.im!");
|
||||
cy.findByRole("button", { name: "Create" }).click();
|
||||
});
|
||||
|
||||
// Create the default General & Random rooms, as well as a custom "Jokes" room
|
||||
cy.findByPlaceholderText("General").should("exist");
|
||||
cy.findByPlaceholderText("Random").should("exist");
|
||||
cy.findByPlaceholderText("Support").type("Jokes");
|
||||
cy.findByRole("button", { name: "Continue" }).click();
|
||||
|
||||
// Copy matrix.to link
|
||||
// Regex pattern due to strings of "mx_SpacePublicShare_shareButton"
|
||||
cy.findByRole("button", { name: /Share invite link/ }).realClick();
|
||||
cy.getClipboardText().should("eq", "https://matrix.to/#/#lets-have-a-riot:localhost");
|
||||
|
||||
// Go to space home
|
||||
cy.findByRole("button", { name: "Go to my first room" }).click();
|
||||
|
||||
// Assert rooms exist in the room list
|
||||
cy.findByRole("treeitem", { name: "General" }).should("exist");
|
||||
cy.findByRole("treeitem", { name: "Random" }).should("exist");
|
||||
cy.findByRole("treeitem", { name: "Jokes" }).should("exist");
|
||||
});
|
||||
|
||||
it("should allow user to create private space", () => {
|
||||
openSpaceCreateMenu().within(() => {
|
||||
// Regex pattern due to strings of "mx_SpaceCreateMenuType_private"
|
||||
cy.findByRole("button", { name: /Private/ }).click();
|
||||
|
||||
cy.get('.mx_SpaceBasicSettings_avatarContainer input[type="file"]').selectFile(
|
||||
"cypress/fixtures/riot.png",
|
||||
{ force: true },
|
||||
);
|
||||
cy.findByRole("textbox", { name: "Name" }).type("This is not a Riot");
|
||||
cy.findByRole("textbox", { name: "Address" }).should("not.exist");
|
||||
cy.findByRole("textbox", { name: "Description" }).type("This is a private space of mourning Riot.im...");
|
||||
cy.findByRole("button", { name: "Create" }).click();
|
||||
});
|
||||
|
||||
// Regex pattern due to strings of "mx_SpaceRoomView_privateScope_meAndMyTeammatesButton"
|
||||
cy.findByRole("button", { name: /Me and my teammates/ }).click();
|
||||
|
||||
// Create the default General & Random rooms, as well as a custom "Projects" room
|
||||
cy.findByPlaceholderText("General").should("exist");
|
||||
cy.findByPlaceholderText("Random").should("exist");
|
||||
cy.findByPlaceholderText("Support").type("Projects");
|
||||
cy.findByRole("button", { name: "Continue" }).click();
|
||||
|
||||
cy.get(".mx_SpaceRoomView h1").findByText("Invite your teammates");
|
||||
cy.get(".mx_SpaceRoomView").percySnapshotElement("Space - 'Invite your teammates' dialog");
|
||||
cy.findByRole("button", { name: "Skip for now" }).click();
|
||||
|
||||
// Assert rooms exist in the room list
|
||||
cy.findByRole("treeitem", { name: "General" }).should("exist");
|
||||
cy.findByRole("treeitem", { name: "Random" }).should("exist");
|
||||
cy.findByRole("treeitem", { name: "Projects" }).should("exist");
|
||||
|
||||
// Assert rooms exist in the space explorer
|
||||
cy.contains(".mx_SpaceHierarchy_list .mx_SpaceHierarchy_roomTile", "General").should("exist");
|
||||
cy.contains(".mx_SpaceHierarchy_list .mx_SpaceHierarchy_roomTile", "Random").should("exist");
|
||||
cy.contains(".mx_SpaceHierarchy_list .mx_SpaceHierarchy_roomTile", "Projects").should("exist");
|
||||
});
|
||||
|
||||
it("should allow user to create just-me space", () => {
|
||||
cy.createRoom({
|
||||
name: "Sample Room",
|
||||
});
|
||||
|
||||
openSpaceCreateMenu().within(() => {
|
||||
// Regex pattern due to strings of "mx_SpaceCreateMenuType_private"
|
||||
cy.findByRole("button", { name: /Private/ }).click();
|
||||
|
||||
cy.get('.mx_SpaceBasicSettings_avatarContainer input[type="file"]').selectFile(
|
||||
"cypress/fixtures/riot.png",
|
||||
{ force: true },
|
||||
);
|
||||
cy.findByRole("textbox", { name: "Address" }).should("not.exist");
|
||||
cy.findByRole("textbox", { name: "Description" }).type("This is a personal space to mourn Riot.im...");
|
||||
cy.findByRole("textbox", { name: "Name" }).type("This is my Riot{enter}");
|
||||
});
|
||||
|
||||
// Regex pattern due to of strings of "mx_SpaceRoomView_privateScope_justMeButton"
|
||||
cy.findByRole("button", { name: /Just me/ }).click();
|
||||
|
||||
cy.findByText("Sample Room").click({ force: true }); // force click as checkbox size is zero
|
||||
|
||||
// Temporal implementation as multiple elements with the role "button" and name "Add" are found
|
||||
cy.get(".mx_AddExistingToSpace_footer").within(() => {
|
||||
cy.findByRole("button", { name: "Add" }).click();
|
||||
});
|
||||
|
||||
cy.get(".mx_SpaceHierarchy_list").within(() => {
|
||||
// Regex pattern due to the strings of "mx_SpaceHierarchy_roomTile_joined"
|
||||
cy.findByRole("treeitem", { name: /Sample Room/ }).should("exist");
|
||||
});
|
||||
});
|
||||
|
||||
it("should allow user to invite another to a space", () => {
|
||||
let bot: MatrixClient;
|
||||
cy.getBot(homeserver, { displayName: "BotBob" }).then((_bot) => {
|
||||
bot = _bot;
|
||||
});
|
||||
|
||||
cy.createSpace({
|
||||
visibility: "public" as any,
|
||||
room_alias_name: "space",
|
||||
}).as("spaceId");
|
||||
|
||||
openSpaceContextMenu("#space:localhost").within(() => {
|
||||
cy.findByRole("menuitem", { name: "Invite" }).click();
|
||||
});
|
||||
|
||||
cy.get(".mx_SpacePublicShare").within(() => {
|
||||
// Copy link first
|
||||
// Regex pattern due to strings of "mx_SpacePublicShare_shareButton"
|
||||
cy.findByRole("button", { name: /Share invite link/ })
|
||||
.focus()
|
||||
.realClick();
|
||||
cy.getClipboardText().should("eq", "https://matrix.to/#/#space:localhost");
|
||||
// Start Matrix invite flow
|
||||
// Regex pattern due to strings of "mx_SpacePublicShare_inviteButton"
|
||||
cy.findByRole("button", { name: /Invite people/ }).click();
|
||||
});
|
||||
|
||||
cy.get(".mx_InviteDialog_other").within(() => {
|
||||
cy.findByRole("textbox").type(bot.getUserId());
|
||||
cy.findByRole("button", { name: "Invite" }).click();
|
||||
});
|
||||
|
||||
cy.get(".mx_InviteDialog_other").should("not.exist");
|
||||
});
|
||||
|
||||
it("should show space invites at the top of the space panel", () => {
|
||||
cy.createSpace({
|
||||
name: "My Space",
|
||||
});
|
||||
cy.getSpacePanelButton("My Space").should("exist");
|
||||
|
||||
cy.getBot(homeserver, { displayName: "BotBob" }).then({ timeout: 10000 }, async (bot) => {
|
||||
const { room_id: roomId } = await bot.createRoom(spaceCreateOptions("Space Space"));
|
||||
await bot.invite(roomId, user.userId);
|
||||
});
|
||||
// Assert that `Space Space` is above `My Space` due to it being an invite
|
||||
cy.getSpacePanelButton("Space Space")
|
||||
.should("exist")
|
||||
.parent()
|
||||
.next()
|
||||
.findByRole("button", { name: "My Space" })
|
||||
.should("exist");
|
||||
});
|
||||
|
||||
it("should include rooms in space home", () => {
|
||||
cy.createRoom({
|
||||
name: "Music",
|
||||
}).as("roomId1");
|
||||
cy.createRoom({
|
||||
name: "Gaming",
|
||||
}).as("roomId2");
|
||||
|
||||
const spaceName = "Spacey Mc. Space Space";
|
||||
cy.all([cy.get<string>("@roomId1"), cy.get<string>("@roomId2")]).then(([roomId1, roomId2]) => {
|
||||
cy.createSpace({
|
||||
name: spaceName,
|
||||
initial_state: [spaceChildInitialState(roomId1), spaceChildInitialState(roomId2)],
|
||||
}).as("spaceId");
|
||||
});
|
||||
|
||||
cy.get("@spaceId").then(() => {
|
||||
cy.viewSpaceHomeByName(spaceName);
|
||||
});
|
||||
cy.get(".mx_SpaceRoomView .mx_SpaceHierarchy_list").within(() => {
|
||||
// Regex pattern due to strings in "mx_SpaceHierarchy_roomTile_name"
|
||||
cy.findByRole("treeitem", { name: /Music/ }).findByRole("button").should("exist");
|
||||
cy.findByRole("treeitem", { name: /Gaming/ })
|
||||
.findByRole("button")
|
||||
.should("exist");
|
||||
});
|
||||
});
|
||||
|
||||
it("should render subspaces in the space panel only when expanded", () => {
|
||||
cy.injectAxe();
|
||||
|
||||
cy.createSpace({
|
||||
name: "Child Space",
|
||||
initial_state: [],
|
||||
}).then((spaceId) => {
|
||||
cy.createSpace({
|
||||
name: "Root Space",
|
||||
initial_state: [spaceChildInitialState(spaceId)],
|
||||
}).as("spaceId");
|
||||
});
|
||||
|
||||
// Find collapsed Space panel
|
||||
cy.findByRole("tree", { name: "Spaces" }).within(() => {
|
||||
cy.findByRole("button", { name: "Root Space" }).should("exist");
|
||||
cy.findByRole("button", { name: "Child Space" }).should("not.exist");
|
||||
});
|
||||
|
||||
const axeOptions = {
|
||||
rules: {
|
||||
// Disable this check as it triggers on nested roving tab index elements which are in practice fine
|
||||
"nested-interactive": {
|
||||
enabled: false,
|
||||
},
|
||||
// Disable this check as it wrongly triggers on the room list container which has
|
||||
// roving tab index elements with automatic scrolling
|
||||
"scrollable-region-focusable": {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
cy.checkA11y(undefined, axeOptions);
|
||||
cy.get(".mx_SpacePanel").percySnapshotElement("Space panel collapsed", { widths: [68] });
|
||||
|
||||
cy.findByRole("tree", { name: "Spaces" }).within(() => {
|
||||
// This finds the expand button with the class name "mx_SpaceButton_toggleCollapse". Note there is another
|
||||
// button with the same name with different class name "mx_SpacePanel_toggleCollapse".
|
||||
cy.findByRole("button", { name: "Expand" }).realHover().click();
|
||||
});
|
||||
cy.get(".mx_SpacePanel:not(.collapsed)").should("exist"); // TODO: replace :not() selector
|
||||
|
||||
cy.contains(".mx_SpaceItem", "Root Space")
|
||||
.should("exist")
|
||||
.contains(".mx_SpaceItem", "Child Space")
|
||||
.should("exist");
|
||||
|
||||
cy.checkA11y(undefined, axeOptions);
|
||||
cy.get(".mx_SpacePanel").percySnapshotElement("Space panel expanded", { widths: [258] });
|
||||
});
|
||||
|
||||
it("should not soft crash when joining a room from space hierarchy which has a link in its topic", () => {
|
||||
cy.getBot(homeserver, { displayName: "BotBob" }).then({ timeout: 10000 }, async (bot) => {
|
||||
const { room_id: roomId } = await bot.createRoom({
|
||||
preset: "public_chat" as Preset,
|
||||
name: "Test Room",
|
||||
topic: "This is a topic https://github.com/matrix-org/matrix-react-sdk/pull/10060 with a link",
|
||||
});
|
||||
const { room_id: spaceId } = await bot.createRoom(spaceCreateOptions("Test Space", [roomId]));
|
||||
await bot.invite(spaceId, user.userId);
|
||||
});
|
||||
|
||||
cy.getSpacePanelButton("Test Space").should("exist");
|
||||
cy.wait(500); // without this we can end up clicking too quickly and it ends up having no effect
|
||||
cy.viewSpaceByName("Test Space");
|
||||
cy.findByRole("button", { name: "Accept" }).click();
|
||||
|
||||
// Regex pattern due to strings in "mx_SpaceHierarchy_roomTile_item"
|
||||
cy.findByRole("button", { name: /Test Room/ }).realHover();
|
||||
cy.findByRole("button", { name: "Join" }).should("exist").realHover().click();
|
||||
cy.findByRole("button", { name: "View", timeout: 5000 }).should("exist").realHover().click();
|
||||
|
||||
// Assert we get shown the new room intro, and thus not the soft crash screen
|
||||
cy.get(".mx_NewRoomIntro").should("exist");
|
||||
});
|
||||
});
|
|
@ -39,27 +39,6 @@ declare global {
|
|||
* @param id
|
||||
*/
|
||||
viewRoomById(id: string): void;
|
||||
|
||||
/**
|
||||
* Returns the space panel space button based on a name. The space
|
||||
* must be visible in the space panel
|
||||
* @param name The space name to find
|
||||
*/
|
||||
getSpacePanelButton(name: string): Chainable<JQuery<HTMLElement>>;
|
||||
|
||||
/**
|
||||
* Opens the given space home by name. The space must be visible in
|
||||
* the space list.
|
||||
* @param name The space name to find and click on/open.
|
||||
*/
|
||||
viewSpaceHomeByName(name: string): Chainable<JQuery<HTMLElement>>;
|
||||
|
||||
/**
|
||||
* Opens the given space by name. The space must be visible in the
|
||||
* space list.
|
||||
* @param name The space name to find and click on/open.
|
||||
*/
|
||||
viewSpaceByName(name: string): Chainable<JQuery<HTMLElement>>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,17 +64,5 @@ Cypress.Commands.add("viewRoomById", (id: string): void => {
|
|||
cy.visit(`/#/room/${id}`);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("getSpacePanelButton", (name: string): Chainable<JQuery<HTMLElement>> => {
|
||||
return cy.findByRole("button", { name: name }).should("have.class", "mx_SpaceButton");
|
||||
});
|
||||
|
||||
Cypress.Commands.add("viewSpaceByName", (name: string): Chainable<JQuery<HTMLElement>> => {
|
||||
return cy.getSpacePanelButton(name).click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add("viewSpaceHomeByName", (name: string): Chainable<JQuery<HTMLElement>> => {
|
||||
return cy.getSpacePanelButton(name).dblclick();
|
||||
});
|
||||
|
||||
// Needed to make this file a module
|
||||
export {};
|
||||
|
|
|
@ -26,6 +26,7 @@ export default defineConfig<TestOptions>({
|
|||
ignoreHTTPSErrors: true,
|
||||
video: "retain-on-failure",
|
||||
baseURL,
|
||||
permissions: ["clipboard-write", "clipboard-read"],
|
||||
},
|
||||
webServer: {
|
||||
command: process.env.CI ? "npx serve -p 8080 -L ../webapp" : "yarn --cwd ../element-web start",
|
||||
|
|
|
@ -0,0 +1,296 @@
|
|||
/*
|
||||
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 { Locator, Page } from "@playwright/test";
|
||||
import { test, expect } from "../../element-web-test";
|
||||
import type { Preset, ICreateRoomOpts } from "matrix-js-sdk/src/matrix";
|
||||
import { ElementAppPage } from "../../pages/ElementAppPage";
|
||||
|
||||
async function openSpaceCreateMenu(page: Page): Promise<Locator> {
|
||||
await page.getByRole("button", { name: "Create a space" }).click();
|
||||
return page.locator(".mx_SpaceCreateMenu_wrapper .mx_ContextualMenu");
|
||||
}
|
||||
|
||||
async function openSpaceContextMenu(page: Page, app: ElementAppPage, spaceName: string): Promise<Locator> {
|
||||
const button = await app.getSpacePanelButton(spaceName);
|
||||
await button.click({ button: "right" });
|
||||
return page.locator(".mx_SpacePanel_contextMenu");
|
||||
}
|
||||
|
||||
function spaceCreateOptions(spaceName: string, roomIds: string[] = []): ICreateRoomOpts {
|
||||
return {
|
||||
creation_content: {
|
||||
type: "m.space",
|
||||
},
|
||||
initial_state: [
|
||||
{
|
||||
type: "m.room.name",
|
||||
content: {
|
||||
name: spaceName,
|
||||
},
|
||||
},
|
||||
...roomIds.map(spaceChildInitialState),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function spaceChildInitialState(roomId: string): ICreateRoomOpts["initial_state"]["0"] {
|
||||
return {
|
||||
type: "m.space.child",
|
||||
state_key: roomId,
|
||||
content: {
|
||||
via: [roomId.split(":")[1]],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test.describe("Spaces", () => {
|
||||
test.use({
|
||||
displayName: "Sue",
|
||||
botCreateOpts: { displayName: "BotBob" },
|
||||
});
|
||||
|
||||
test("should allow user to create public space", async ({ page, app, user }) => {
|
||||
const contextMenu = await openSpaceCreateMenu(page);
|
||||
await expect(contextMenu).toMatchScreenshot("space-create-menu.png");
|
||||
|
||||
await contextMenu.getByRole("button", { name: /Public/ }).click();
|
||||
|
||||
await contextMenu
|
||||
.locator('.mx_SpaceBasicSettings_avatarContainer input[type="file"]')
|
||||
.setInputFiles("cypress/fixtures/riot.png");
|
||||
await contextMenu.getByRole("textbox", { name: "Name" }).fill("Let's have a Riot");
|
||||
await expect(contextMenu.getByRole("textbox", { name: "Address" })).toHaveValue("lets-have-a-riot");
|
||||
await contextMenu.getByRole("textbox", { name: "Description" }).fill("This is a space to reminisce Riot.im!");
|
||||
await contextMenu.getByRole("button", { name: "Create" }).click();
|
||||
|
||||
// Create the default General & Random rooms, as well as a custom "Jokes" room
|
||||
await expect(page.getByPlaceholder("General")).toBeVisible();
|
||||
await expect(page.getByPlaceholder("Random")).toBeVisible();
|
||||
await page.getByPlaceholder("Support").fill("Jokes");
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
|
||||
// Copy matrix.to link
|
||||
await page.getByRole("button", { name: "Share invite link" }).click();
|
||||
expect(await app.getClipboardText()).toEqual("https://matrix.to/#/#lets-have-a-riot:localhost");
|
||||
|
||||
// Go to space home
|
||||
await page.getByRole("button", { name: "Go to my first room" }).click();
|
||||
|
||||
// Assert rooms exist in the room list
|
||||
await expect(page.getByRole("treeitem", { name: "General" })).toBeVisible();
|
||||
await expect(page.getByRole("treeitem", { name: "Random" })).toBeVisible();
|
||||
await expect(page.getByRole("treeitem", { name: "Jokes" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("should allow user to create private space", async ({ page, app, user }) => {
|
||||
const menu = await openSpaceCreateMenu(page);
|
||||
await menu.getByRole("button", { name: "Private" }).click();
|
||||
|
||||
await menu
|
||||
.locator('.mx_SpaceBasicSettings_avatarContainer input[type="file"]')
|
||||
.setInputFiles("cypress/fixtures/riot.png");
|
||||
await menu.getByRole("textbox", { name: "Name" }).fill("This is not a Riot");
|
||||
await expect(menu.getByRole("textbox", { name: "Address" })).not.toBeVisible();
|
||||
await menu.getByRole("textbox", { name: "Description" }).fill("This is a private space of mourning Riot.im...");
|
||||
await menu.getByRole("button", { name: "Create" }).click();
|
||||
|
||||
await page.getByRole("button", { name: "Me and my teammates" }).click();
|
||||
|
||||
// Create the default General & Random rooms, as well as a custom "Projects" room
|
||||
await expect(page.getByPlaceholder("General")).toBeVisible();
|
||||
await expect(page.getByPlaceholder("Random")).toBeVisible();
|
||||
await page.getByPlaceholder("Support").fill("Projects");
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
|
||||
await expect(page.locator(".mx_SpaceRoomView h1").getByText("Invite your teammates")).toBeVisible();
|
||||
await expect(page.locator(".mx_SpaceRoomView")).toMatchScreenshot("invite-teammates-dialog.png");
|
||||
await page.getByRole("button", { name: "Skip for now" }).click();
|
||||
|
||||
// Assert rooms exist in the room list
|
||||
await expect(page.getByRole("treeitem", { name: "General", exact: true })).toBeVisible();
|
||||
await expect(page.getByRole("treeitem", { name: "Random", exact: true })).toBeVisible();
|
||||
await expect(page.getByRole("treeitem", { name: "Projects", exact: true })).toBeVisible();
|
||||
|
||||
// Assert rooms exist in the space explorer
|
||||
await expect(
|
||||
page.locator(".mx_SpaceHierarchy_list .mx_SpaceHierarchy_roomTile", { hasText: "General" }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.locator(".mx_SpaceHierarchy_list .mx_SpaceHierarchy_roomTile", { hasText: "Random" }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.locator(".mx_SpaceHierarchy_list .mx_SpaceHierarchy_roomTile", { hasText: "Projects" }),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("should allow user to create just-me space", async ({ page, app, user }) => {
|
||||
await app.client.createRoom({
|
||||
name: "Sample Room",
|
||||
});
|
||||
|
||||
const menu = await openSpaceCreateMenu(page);
|
||||
await menu.getByRole("button", { name: "Private" }).click();
|
||||
|
||||
await menu
|
||||
.locator('.mx_SpaceBasicSettings_avatarContainer input[type="file"]')
|
||||
.setInputFiles("cypress/fixtures/riot.png");
|
||||
await expect(menu.getByRole("textbox", { name: "Address" })).not.toBeVisible();
|
||||
await menu.getByRole("textbox", { name: "Description" }).fill("This is a personal space to mourn Riot.im...");
|
||||
await menu.getByRole("textbox", { name: "Name" }).fill("This is my Riot");
|
||||
await menu.getByRole("textbox", { name: "Name" }).press("Enter");
|
||||
|
||||
await page.getByRole("button", { name: "Just me" }).click();
|
||||
|
||||
await page.getByText("Sample Room").click({ force: true }); // force click as checkbox size is zero
|
||||
|
||||
// Temporal implementation as multiple elements with the role "button" and name "Add" are found
|
||||
await page.locator(".mx_AddExistingToSpace_footer").getByRole("button", { name: "Add" }).click();
|
||||
|
||||
await expect(
|
||||
page.locator(".mx_SpaceHierarchy_list").getByRole("treeitem", { name: "Sample Room" }),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("should allow user to invite another to a space", async ({ page, app, user, bot }) => {
|
||||
await app.client.createSpace({
|
||||
visibility: "public" as any,
|
||||
room_alias_name: "space",
|
||||
});
|
||||
|
||||
const menu = await openSpaceContextMenu(page, app, "#space:localhost");
|
||||
await menu.getByRole("menuitem", { name: "Invite" }).click();
|
||||
|
||||
const shareDialog = page.locator(".mx_SpacePublicShare");
|
||||
// Copy link first
|
||||
await shareDialog.getByRole("button", { name: "Share invite link" }).click();
|
||||
expect(await app.getClipboardText()).toEqual("https://matrix.to/#/#space:localhost");
|
||||
// Start Matrix invite flow
|
||||
await shareDialog.getByRole("button", { name: "Invite people" }).click();
|
||||
|
||||
const otherSection = page.locator(".mx_InviteDialog_other");
|
||||
await otherSection.getByRole("textbox").fill(bot.credentials.userId);
|
||||
await otherSection.getByRole("button", { name: "Invite" }).click();
|
||||
|
||||
await expect(page.locator(".mx_InviteDialog_other")).not.toBeVisible();
|
||||
});
|
||||
|
||||
test("should show space invites at the top of the space panel", async ({ page, app, user, bot }) => {
|
||||
await app.client.createSpace({
|
||||
name: "My Space",
|
||||
});
|
||||
await expect(await app.getSpacePanelButton("My Space")).toBeVisible();
|
||||
|
||||
const roomId = await bot.createRoom(spaceCreateOptions("Space Space"));
|
||||
await bot.inviteUser(roomId, user.userId);
|
||||
|
||||
// Assert that `Space Space` is above `My Space` due to it being an invite
|
||||
const buttons = page.getByRole("tree", { name: "Spaces" }).locator(".mx_SpaceButton");
|
||||
await expect(buttons.nth(1)).toHaveAttribute("aria-label", "Space Space");
|
||||
await expect(buttons.nth(2)).toHaveAttribute("aria-label", "My Space");
|
||||
});
|
||||
|
||||
test("should include rooms in space home", async ({ page, app, user }) => {
|
||||
const roomId1 = await app.client.createRoom({
|
||||
name: "Music",
|
||||
});
|
||||
const roomId2 = await app.client.createRoom({
|
||||
name: "Gaming",
|
||||
});
|
||||
|
||||
const spaceName = "Spacey Mc. Space Space";
|
||||
await app.client.createSpace({
|
||||
name: spaceName,
|
||||
initial_state: [spaceChildInitialState(roomId1), spaceChildInitialState(roomId2)],
|
||||
});
|
||||
|
||||
await app.viewSpaceHomeByName(spaceName);
|
||||
|
||||
const hierarchyList = page.locator(".mx_SpaceRoomView .mx_SpaceHierarchy_list");
|
||||
await expect(hierarchyList.getByRole("treeitem", { name: "Music" }).getByRole("button")).toBeVisible();
|
||||
await expect(hierarchyList.getByRole("treeitem", { name: "Gaming" }).getByRole("button")).toBeVisible();
|
||||
});
|
||||
|
||||
test("should render subspaces in the space panel only when expanded", async ({
|
||||
page,
|
||||
app,
|
||||
user,
|
||||
axe,
|
||||
checkA11y,
|
||||
}) => {
|
||||
axe.disableRules([
|
||||
// Disable this check as it triggers on nested roving tab index elements which are in practice fine
|
||||
"nested-interactive",
|
||||
// XXX: We have some known contrast issues here
|
||||
"color-contrast",
|
||||
]);
|
||||
|
||||
const childSpaceId = await app.client.createSpace({
|
||||
name: "Child Space",
|
||||
initial_state: [],
|
||||
});
|
||||
await app.client.createSpace({
|
||||
name: "Root Space",
|
||||
initial_state: [spaceChildInitialState(childSpaceId)],
|
||||
});
|
||||
|
||||
// Find collapsed Space panel
|
||||
const spaceTree = page.getByRole("tree", { name: "Spaces" });
|
||||
await expect(spaceTree.getByRole("button", { name: "Root Space" })).toBeVisible();
|
||||
await expect(spaceTree.getByRole("button", { name: "Child Space" })).not.toBeVisible();
|
||||
|
||||
await checkA11y();
|
||||
await expect(page.locator(".mx_SpacePanel")).toMatchScreenshot("space-panel-collapsed.png");
|
||||
|
||||
// This finds the expand button with the class name "mx_SpaceButton_toggleCollapse". Note there is another
|
||||
// button with the same name with different class name "mx_SpacePanel_toggleCollapse".
|
||||
await spaceTree.getByRole("button", { name: "Expand" }).click();
|
||||
await expect(page.locator(".mx_SpacePanel:not(.collapsed)")).toBeVisible(); // TODO: replace :not() selector
|
||||
|
||||
const item = page.locator(".mx_SpaceItem", { hasText: "Root Space" });
|
||||
await expect(item).toBeVisible();
|
||||
await expect(item.locator(".mx_SpaceItem", { hasText: "Child Space" })).toBeVisible();
|
||||
|
||||
await checkA11y();
|
||||
await expect(page.locator(".mx_SpacePanel")).toMatchScreenshot("space-panel-expanded.png");
|
||||
});
|
||||
|
||||
test("should not soft crash when joining a room from space hierarchy which has a link in its topic", async ({
|
||||
page,
|
||||
app,
|
||||
user,
|
||||
bot,
|
||||
}) => {
|
||||
const roomId = await bot.createRoom({
|
||||
preset: "public_chat" as Preset,
|
||||
name: "Test Room",
|
||||
topic: "This is a topic https://github.com/matrix-org/matrix-react-sdk/pull/10060 with a link",
|
||||
});
|
||||
const spaceId = await bot.createRoom(spaceCreateOptions("Test Space", [roomId]));
|
||||
await bot.inviteUser(spaceId, user.userId);
|
||||
|
||||
await expect(await app.getSpacePanelButton("Test Space")).toBeVisible();
|
||||
await app.viewSpaceByName("Test Space");
|
||||
await page.getByRole("button", { name: "Accept" }).click();
|
||||
|
||||
await page.getByRole("button", { name: "Test Room" }).hover();
|
||||
await page.getByRole("button", { name: "Join", exact: true }).click();
|
||||
await page.getByRole("button", { name: "View", exact: true }).click();
|
||||
|
||||
// Assert we get shown the new room intro, and thus not the soft crash screen
|
||||
await expect(page.locator(".mx_NewRoomIntro")).toBeVisible();
|
||||
});
|
||||
});
|
|
@ -117,4 +117,18 @@ export class ElementAppPage {
|
|||
const button = await this.getSpacePanelButton(name);
|
||||
return button.dblclick();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the given space by name. The space must be visible in the
|
||||
* space list.
|
||||
* @param name The space name to find and click on/open.
|
||||
*/
|
||||
public async viewSpaceByName(name: string): Promise<void> {
|
||||
const button = await this.getSpacePanelButton(name);
|
||||
return button.click();
|
||||
}
|
||||
|
||||
public async getClipboardText(): Promise<string> {
|
||||
return this.page.evaluate("navigator.clipboard.readText()");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,4 +161,17 @@ export class Client {
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invites the given user to the given room.
|
||||
* @param roomId the id of the room to invite to
|
||||
* @param userId the id of the user to invite
|
||||
*/
|
||||
public async inviteUser(roomId: string, userId: string): Promise<void> {
|
||||
const client = await this.prepareClient();
|
||||
await client.evaluate((client, { roomId, userId }) => client.invite(roomId, userId), {
|
||||
roomId,
|
||||
userId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
Binary file not shown.
After Width: | Height: | Size: 5.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
Loading…
Reference in New Issue