Integrate searching public rooms and people into the new search experience (#8707)
* Implement searching for public rooms and users in new search experience * Implement loading indicator for spotlight results * Moved spotlight dialog into own subfolder * Extract search result avatar into separate component * Build generic new dropdown menu component * Build new network menu based on new network dropdown component * Switch roomdirectory to use new network dropdown * Replace old networkdropdown with new networkdropdown * Added component for public room result details * Extract hooks and subcomponents from SpotlightDialog * Create new hook to get profile info based for an mxid * Add hook to automatically re-request search results * Add hook to prevent out-of-order search results * Extract member sort algorithm from InviteDialog * Keep sorting for non-room results stable * Sort people suggestions using sort algorithm from InviteDialog * Add copy/copied tooltip for invite link option in spotlight * Clamp length of topic for public room results * Add unit test for useDebouncedSearch * Add unit test for useProfileInfo * Create cypress test cases for spotlight dialog * Add test for useLatestResult to prevent out-of-order results
This commit is contained in:
parent
37298d7b1b
commit
5096e7b992
38 changed files with 3520 additions and 1397 deletions
302
cypress/integration/12-spotlight/spotlight.spec.ts
Normal file
302
cypress/integration/12-spotlight/spotlight.spec.ts
Normal file
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
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 { MatrixClient } from "../../global";
|
||||
import { SynapseInstance } from "../../plugins/synapsedocker";
|
||||
import Chainable = Cypress.Chainable;
|
||||
import Loggable = Cypress.Loggable;
|
||||
import Timeoutable = Cypress.Timeoutable;
|
||||
import Withinable = Cypress.Withinable;
|
||||
import Shadow = Cypress.Shadow;
|
||||
|
||||
export enum Filter {
|
||||
People = "people",
|
||||
PublicRooms = "public_rooms"
|
||||
}
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
/**
|
||||
* Opens the spotlight dialog
|
||||
*/
|
||||
openSpotlightDialog(
|
||||
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>
|
||||
): Chainable<JQuery<HTMLElement>>;
|
||||
spotlightDialog(
|
||||
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>
|
||||
): Chainable<JQuery<HTMLElement>>;
|
||||
spotlightFilter(
|
||||
filter: Filter | null,
|
||||
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>
|
||||
): Chainable<JQuery<HTMLElement>>;
|
||||
spotlightSearch(
|
||||
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>
|
||||
): Chainable<JQuery<HTMLElement>>;
|
||||
spotlightResults(
|
||||
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>
|
||||
): Chainable<JQuery<HTMLElement>>;
|
||||
roomHeaderName(
|
||||
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>
|
||||
): Chainable<JQuery<HTMLElement>>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Cypress.Commands.add("openSpotlightDialog", (
|
||||
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>,
|
||||
): Chainable<JQuery<HTMLElement>> => {
|
||||
cy.get('.mx_RoomSearch_spotlightTrigger', options).click({ force: true });
|
||||
return cy.spotlightDialog(options);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("spotlightDialog", (
|
||||
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>,
|
||||
): Chainable<JQuery<HTMLElement>> => {
|
||||
return cy.get('[role=dialog][aria-label="Search Dialog"]', options);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("spotlightFilter", (
|
||||
filter: Filter | null,
|
||||
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>,
|
||||
): Chainable<JQuery<HTMLElement>> => {
|
||||
let selector: string;
|
||||
switch (filter) {
|
||||
case Filter.People:
|
||||
selector = "#mx_SpotlightDialog_button_startChat";
|
||||
break;
|
||||
case Filter.PublicRooms:
|
||||
selector = "#mx_SpotlightDialog_button_explorePublicRooms";
|
||||
break;
|
||||
default:
|
||||
selector = ".mx_SpotlightDialog_filter";
|
||||
break;
|
||||
}
|
||||
return cy.get(selector, options).click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add("spotlightSearch", (
|
||||
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>,
|
||||
): Chainable<JQuery<HTMLElement>> => {
|
||||
return cy.get(".mx_SpotlightDialog_searchBox input", options);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("spotlightResults", (
|
||||
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>,
|
||||
): Chainable<JQuery<HTMLElement>> => {
|
||||
return cy.get(".mx_SpotlightDialog_section.mx_SpotlightDialog_results .mx_SpotlightDialog_option", options);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("roomHeaderName", (
|
||||
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>,
|
||||
): Chainable<JQuery<HTMLElement>> => {
|
||||
return cy.get(".mx_RoomHeader_nametext", options);
|
||||
});
|
||||
|
||||
describe("Spotlight", () => {
|
||||
let synapse: SynapseInstance;
|
||||
|
||||
const bot1Name = "BotBob";
|
||||
let bot1: MatrixClient;
|
||||
|
||||
const bot2Name = "ByteBot";
|
||||
let bot2: MatrixClient;
|
||||
|
||||
const room1Name = "247";
|
||||
let room1Id: string;
|
||||
|
||||
const room2Name = "Lounge";
|
||||
let room2Id: string;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.enableLabsFeature("feature_spotlight");
|
||||
cy.startSynapse("default").then(data => {
|
||||
synapse = data;
|
||||
cy.initTestUser(synapse, "Jim").then(() =>
|
||||
cy.getBot(synapse, bot1Name).then(_bot1 => {
|
||||
bot1 = _bot1;
|
||||
}),
|
||||
).then(() =>
|
||||
cy.getBot(synapse, bot2Name).then(_bot2 => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
bot2 = _bot2;
|
||||
}),
|
||||
).then(() =>
|
||||
cy.window({ log: false }).then(({ matrixcs: { Visibility } }) => {
|
||||
cy.createRoom({ name: room1Name, visibility: Visibility.Public }).then(_room1Id => {
|
||||
room1Id = _room1Id;
|
||||
cy.inviteUser(room1Id, bot1.getUserId());
|
||||
cy.visit("/#/room/" + room1Id);
|
||||
});
|
||||
bot2.createRoom({ name: room2Name, visibility: Visibility.Public })
|
||||
.then(({ room_id: _room2Id }) => {
|
||||
room2Id = _room2Id;
|
||||
bot2.invite(room2Id, bot1.getUserId());
|
||||
});
|
||||
}),
|
||||
).then(() =>
|
||||
cy.get('.mx_RoomSublist_skeletonUI').should('not.exist'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopSynapse(synapse);
|
||||
});
|
||||
|
||||
it("should be able to add and remove filters via keyboard", () => {
|
||||
cy.openSpotlightDialog().within(() => {
|
||||
cy.spotlightSearch().type("{downArrow}");
|
||||
cy.get("#mx_SpotlightDialog_button_explorePublicRooms").should("have.attr", "aria-selected", "true");
|
||||
cy.spotlightSearch().type("{enter}");
|
||||
cy.get(".mx_SpotlightDialog_filter").should("contain", "Public rooms");
|
||||
cy.spotlightSearch().type("{backspace}");
|
||||
cy.get(".mx_SpotlightDialog_filter").should("not.exist");
|
||||
|
||||
cy.spotlightSearch().type("{downArrow}");
|
||||
cy.spotlightSearch().type("{downArrow}");
|
||||
cy.get("#mx_SpotlightDialog_button_startChat").should("have.attr", "aria-selected", "true");
|
||||
cy.spotlightSearch().type("{enter}");
|
||||
cy.get(".mx_SpotlightDialog_filter").should("contain", "People");
|
||||
cy.spotlightSearch().type("{backspace}");
|
||||
cy.get(".mx_SpotlightDialog_filter").should("not.exist");
|
||||
});
|
||||
});
|
||||
|
||||
it("should find joined rooms", () => {
|
||||
cy.openSpotlightDialog().within(() => {
|
||||
cy.spotlightSearch().clear().type(room1Name);
|
||||
cy.spotlightResults().should("have.length", 1);
|
||||
cy.spotlightResults().eq(0).should("contain", room1Name);
|
||||
cy.spotlightResults().eq(0).click();
|
||||
cy.url().should("contain", room1Id);
|
||||
}).then(() => {
|
||||
cy.roomHeaderName().should("contain", room1Name);
|
||||
});
|
||||
});
|
||||
|
||||
it("should find known public rooms", () => {
|
||||
cy.openSpotlightDialog().within(() => {
|
||||
cy.spotlightFilter(Filter.PublicRooms);
|
||||
cy.spotlightSearch().clear().type(room1Name);
|
||||
cy.spotlightResults().should("have.length", 1);
|
||||
cy.spotlightResults().eq(0).should("contain", room1Name);
|
||||
cy.spotlightResults().eq(0).click();
|
||||
cy.url().should("contain", room1Id);
|
||||
}).then(() => {
|
||||
cy.roomHeaderName().should("contain", room1Name);
|
||||
});
|
||||
});
|
||||
|
||||
it("should find unknown public rooms", () => {
|
||||
cy.openSpotlightDialog().within(() => {
|
||||
cy.spotlightFilter(Filter.PublicRooms);
|
||||
cy.spotlightSearch().clear().type(room2Name);
|
||||
cy.spotlightResults().should("have.length", 1);
|
||||
cy.spotlightResults().eq(0).should("contain", room2Name);
|
||||
cy.spotlightResults().eq(0).click();
|
||||
cy.url().should("contain", room2Id);
|
||||
}).then(() => {
|
||||
cy.get(".mx_RoomPreviewBar_actions .mx_AccessibleButton").click();
|
||||
cy.roomHeaderName().should("contain", room2Name);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: We currently can’t test finding rooms on other homeservers/other protocols
|
||||
// We obviously don’t have federation or bridges in cypress tests
|
||||
/*
|
||||
const room3Name = "Matrix HQ";
|
||||
const room3Id = "#matrix:matrix.org";
|
||||
|
||||
it("should find unknown public rooms on other homeservers", () => {
|
||||
cy.openSpotlightDialog().within(() => {
|
||||
cy.spotlightFilter(Filter.PublicRooms);
|
||||
cy.spotlightSearch().clear().type(room3Name);
|
||||
cy.get("[aria-haspopup=true][role=button]").click();
|
||||
}).then(() => {
|
||||
cy.contains(".mx_GenericDropdownMenu_Option--header", "matrix.org")
|
||||
.next("[role=menuitemradio]")
|
||||
.click();
|
||||
cy.wait(3_600_000);
|
||||
}).then(() => cy.spotlightDialog().within(() => {
|
||||
cy.spotlightResults().should("have.length", 1);
|
||||
cy.spotlightResults().eq(0).should("contain", room3Name);
|
||||
cy.spotlightResults().eq(0).should("contain", room3Id);
|
||||
}));
|
||||
});
|
||||
*/
|
||||
it("should find known people", () => {
|
||||
cy.openSpotlightDialog().within(() => {
|
||||
cy.spotlightFilter(Filter.People);
|
||||
cy.spotlightSearch().clear().type(bot1Name);
|
||||
cy.spotlightResults().should("have.length", 1);
|
||||
cy.spotlightResults().eq(0).should("contain", bot1Name);
|
||||
cy.spotlightResults().eq(0).click();
|
||||
}).then(() => {
|
||||
cy.roomHeaderName().should("contain", bot1Name);
|
||||
});
|
||||
});
|
||||
|
||||
it("should find unknown people", () => {
|
||||
cy.openSpotlightDialog().within(() => {
|
||||
cy.spotlightFilter(Filter.People);
|
||||
cy.spotlightSearch().clear().type(bot2Name);
|
||||
cy.spotlightResults().should("have.length", 1);
|
||||
cy.spotlightResults().eq(0).should("contain", bot2Name);
|
||||
cy.spotlightResults().eq(0).click();
|
||||
}).then(() => {
|
||||
cy.roomHeaderName().should("contain", bot2Name);
|
||||
});
|
||||
});
|
||||
|
||||
it("should allow opening group chat dialog", () => {
|
||||
cy.openSpotlightDialog().within(() => {
|
||||
cy.spotlightFilter(Filter.People);
|
||||
cy.spotlightSearch().clear().type(bot2Name);
|
||||
cy.spotlightResults().should("have.length", 1);
|
||||
cy.spotlightResults().eq(0).should("contain", bot2Name);
|
||||
cy.get(".mx_SpotlightDialog_startGroupChat").should("contain", "Start a group chat");
|
||||
cy.get(".mx_SpotlightDialog_startGroupChat").click();
|
||||
}).then(() => {
|
||||
cy.get('[role=dialog]').should("contain", "Direct Messages");
|
||||
});
|
||||
});
|
||||
|
||||
it("should be able to navigate results via keyboard", () => {
|
||||
cy.openSpotlightDialog().within(() => {
|
||||
cy.spotlightFilter(Filter.People);
|
||||
cy.spotlightSearch().clear().type("b");
|
||||
cy.spotlightResults().should("have.length", 2);
|
||||
cy.spotlightResults().eq(0).should("have.attr", "aria-selected", "true");
|
||||
cy.spotlightResults().eq(1).should("have.attr", "aria-selected", "false");
|
||||
cy.spotlightSearch().type("{downArrow}");
|
||||
cy.spotlightResults().eq(0).should("have.attr", "aria-selected", "false");
|
||||
cy.spotlightResults().eq(1).should("have.attr", "aria-selected", "true");
|
||||
cy.spotlightSearch().type("{downArrow}");
|
||||
cy.spotlightResults().eq(0).should("have.attr", "aria-selected", "false");
|
||||
cy.spotlightResults().eq(1).should("have.attr", "aria-selected", "false");
|
||||
cy.spotlightSearch().type("{upArrow}");
|
||||
cy.spotlightResults().eq(0).should("have.attr", "aria-selected", "false");
|
||||
cy.spotlightResults().eq(1).should("have.attr", "aria-selected", "true");
|
||||
cy.spotlightSearch().type("{upArrow}");
|
||||
cy.spotlightResults().eq(0).should("have.attr", "aria-selected", "true");
|
||||
cy.spotlightResults().eq(1).should("have.attr", "aria-selected", "false");
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue