diff --git a/cypress/global.d.ts b/cypress/global.d.ts index a3e91a2b44..de4c10a895 100644 --- a/cypress/global.d.ts +++ b/cypress/global.d.ts @@ -15,9 +15,17 @@ limitations under the License. */ import "matrix-js-sdk/src/@types/global"; -import type { MatrixClient, ClientEvent } from "matrix-js-sdk/src/client"; -import type { MatrixScheduler, MemoryCryptoStore, MemoryStore, RoomStateEvent } from "matrix-js-sdk/src/matrix"; -import type { RoomMemberEvent } from "matrix-js-sdk/src/models/room-member"; +import type { + MatrixClient, + ClientEvent, + MatrixScheduler, + MemoryCryptoStore, + MemoryStore, + Preset, + RoomStateEvent, + Visibility, + RoomMemberEvent, +} from "matrix-js-sdk/src/matrix"; import type { WebStorageSessionStore } from "matrix-js-sdk/src/store/session/webstorage"; import type { MatrixDispatcher } from "../src/dispatcher/dispatcher"; import type PerformanceMonitor from "../src/performance"; @@ -42,6 +50,8 @@ declare global { MemoryStore: typeof MemoryStore; MemoryCryptoStore: typeof MemoryCryptoStore; WebStorageSessionStore: typeof WebStorageSessionStore; + Visibility: typeof Visibility; + Preset: typeof Preset; }; } } diff --git a/cypress/integration/11-room-directory/room-directory.spec.ts b/cypress/integration/11-room-directory/room-directory.spec.ts new file mode 100644 index 0000000000..e7e3c5c9c8 --- /dev/null +++ b/cypress/integration/11-room-directory/room-directory.spec.ts @@ -0,0 +1,103 @@ +/* +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 { SynapseInstance } from "../../plugins/synapsedocker"; +import { MatrixClient } from "../../global"; + +describe("Room Directory", () => { + let synapse: SynapseInstance; + + beforeEach(() => { + cy.startSynapse("default").then(data => { + synapse = data; + + cy.initTestUser(synapse, "Ray"); + cy.getBot(synapse, "Paul").as("bot"); + }); + }); + + afterEach(() => { + cy.stopSynapse(synapse); + }); + + it("should allow admin to add alias & publish room to directory", () => { + cy.window({ log: false }).then(win => { + cy.createRoom({ + name: "Gaming", + preset: win.matrixcs.Preset.PublicChat, + }).as("roomId"); + }); + + cy.viewRoomByName("Gaming"); + cy.openRoomSettings(); + + // First add a local address `gaming` + cy.contains(".mx_SettingsFieldset", "Local Addresses").within(() => { + cy.get(".mx_Field input").type("gaming"); + cy.contains(".mx_AccessibleButton", "Add").click(); + cy.get(".mx_EditableItem_item").should("contain", "#gaming:localhost"); + }); + + // Publish into the public rooms directory + cy.contains(".mx_SettingsFieldset", "Published Addresses").within(() => { + cy.get("#canonicalAlias").find(":selected").should("contain", "#gaming:localhost"); + cy.get(`[aria-label="Publish this room to the public in localhost's room directory?"]`).click() + .should("have.attr", "aria-checked", "true"); + }); + + cy.closeDialog(); + + cy.all([ + cy.get("@bot"), + cy.get("@roomId"), + ]).then(async ([bot, roomId]) => { + const resp = await bot.publicRooms({}); + expect(resp.total_room_count_estimate).to.equal(1); + expect(resp.chunk).to.have.length(1); + expect(resp.chunk[0].room_id).to.equal(roomId); + }); + }); + + it("should allow finding published rooms in directory", () => { + const name = "This is a public room"; + cy.all([ + cy.window({ log: false }), + cy.get("@bot"), + ]).then(([win, bot]) => { + bot.createRoom({ + visibility: win.matrixcs.Visibility.Public, + name, + room_alias_name: "test1234", + }); + }); + + cy.get('[role="button"][aria-label="Explore rooms"]').click(); + + cy.get('.mx_RoomDirectory_dialogWrapper [name="dirsearch"]').type("Unknown Room"); + cy.get(".mx_RoomDirectory_dialogWrapper h5").should("contain", 'No results for "Unknown Room"'); + cy.get(".mx_RoomDirectory_dialogWrapper").percySnapshotElement("Room Directory - filtered no results"); + + cy.get('.mx_RoomDirectory_dialogWrapper [name="dirsearch"]').type("{selectAll}{backspace}test1234"); + cy.get(".mx_RoomDirectory_dialogWrapper").contains(".mx_RoomDirectory_listItem", name) + .should("exist").as("resultRow"); + cy.get(".mx_RoomDirectory_dialogWrapper").percySnapshotElement("Room Directory - filtered one result"); + cy.get("@resultRow").find(".mx_AccessibleButton").contains("Join").click(); + + cy.url().should('contain', `/#/room/#test1234:localhost`); + }); +}); diff --git a/cypress/support/percy.ts b/cypress/support/percy.ts index 19f60451e1..f190e7a514 100644 --- a/cypress/support/percy.ts +++ b/cypress/support/percy.ts @@ -37,7 +37,7 @@ declare global { } } -Cypress.Commands.add("percySnapshotElement", { prevSubject: true }, (subject, name, options) => { +Cypress.Commands.add("percySnapshotElement", { prevSubject: "element" }, (subject, name, options) => { cy.percySnapshot(name, { domTransformation: documentClone => scope(documentClone, subject.selector), ...options, diff --git a/cypress/support/settings.ts b/cypress/support/settings.ts index 4be44e2711..aed0631354 100644 --- a/cypress/support/settings.ts +++ b/cypress/support/settings.ts @@ -33,16 +33,22 @@ declare global { */ openUserSettings(tab?: string): Chainable>; + /** + * Open room settings (via room header menu), returning a handle to the resulting dialog. + * @param tab the name of the tab to switch to after opening, optional. + */ + openRoomSettings(tab?: string): Chainable>; + /** * Switch settings tab to the one by the given name, ideally call this in the context of the dialog. * @param tab the name of the tab to switch to. */ - switchTabUserSettings(tab: string): Chainable>; + switchTab(tab: string): Chainable>; /** - * Close user settings, ideally call this in the context of the dialog. + * Close dialog, ideally call this in the context of the dialog. */ - closeUserSettings(): Chainable>; + closeDialog(): Chainable>; /** * Join the given beta, the `Labs` tab must already be opened, @@ -72,18 +78,30 @@ Cypress.Commands.add("openUserSettings", (tab?: string): Chainable { if (tab) { - cy.switchTabUserSettings(tab); + cy.switchTab(tab); } }); }); -Cypress.Commands.add("switchTabUserSettings", (tab: string): Chainable> => { +Cypress.Commands.add("openRoomSettings", (tab?: string): Chainable> => { + cy.get(".mx_RoomHeader_name").click(); + cy.get(".mx_RoomTile_contextMenu").within(() => { + cy.get('[aria-label="Settings"]').click(); + }); + return cy.get(".mx_RoomSettingsDialog").within(() => { + if (tab) { + cy.switchTab(tab); + } + }); +}); + +Cypress.Commands.add("switchTab", (tab: string): Chainable> => { return cy.get(".mx_TabbedView_tabLabels").within(() => { cy.get(".mx_TabbedView_tabLabel").contains(tab).click(); }); }); -Cypress.Commands.add("closeUserSettings", (): Chainable> => { +Cypress.Commands.add("closeDialog", (): Chainable> => { return cy.get('[aria-label="Close dialog"]').click(); }); diff --git a/res/css/views/avatars/_BaseAvatar.scss b/res/css/views/avatars/_BaseAvatar.scss index 16261f000e..802a4235c1 100644 --- a/res/css/views/avatars/_BaseAvatar.scss +++ b/res/css/views/avatars/_BaseAvatar.scss @@ -52,3 +52,12 @@ limitations under the License. vertical-align: top; background-color: $background; } + +// Percy screenshot test specific CSS +@media only percy { + .mx_BaseAvatar_initial, + .mx_BaseAvatar_initial + .mx_BaseAvatar_image { + // Stick the default room avatar colour, so it doesn't cause a false diff on the screenshot + background-color: $username-variant2-color !important; + } +} diff --git a/test/end-to-end-tests/src/scenario.ts b/test/end-to-end-tests/src/scenario.ts index 31855f29e8..1c81205e27 100644 --- a/test/end-to-end-tests/src/scenario.ts +++ b/test/end-to-end-tests/src/scenario.ts @@ -18,7 +18,6 @@ limitations under the License. import { range } from './util'; import { signup } from './usecases/signup'; import { toastScenarios } from './scenarios/toast'; -import { roomDirectoryScenarios } from './scenarios/directory'; import { lazyLoadingScenarios } from './scenarios/lazy-loading'; import { e2eEncryptionScenarios } from './scenarios/e2e-encryption'; import { ElementSession } from "./session"; @@ -45,7 +44,6 @@ export async function scenario(createSession: (s: string) => Promise