Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Integrate searching public rooms and people into the new search experience #8707

Merged
merged 88 commits into from
Jun 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
1d3a2f6
Extract search result avatar into separate component
justjanne May 25, 2022
34e3a9a
Create new hook to get profile info based for an mxid
justjanne May 25, 2022
66e693e
Keep sorting for non-room results stable
justjanne May 25, 2022
691caf3
Build generic new dropdown menu component
justjanne May 27, 2022
cc7342d
Build new network menu based on new network dropdown component
justjanne May 27, 2022
fd21e9e
Moved spotlight dialog into own subfolder
justjanne May 27, 2022
6bfdf58
Extract hooks and subcomponents from SpotlightDialog
justjanne May 27, 2022
315ce5a
Add wrapped hooks for public room, users and profile info results
justjanne May 27, 2022
531f5c0
Added component for public room result details
justjanne May 27, 2022
73c88da
Return public room results even without search query
justjanne May 27, 2022
567e4f6
Implement searching for public rooms and users in new search experience
justjanne May 27, 2022
1d8781c
Add copy/copied tooltip for invite link option in spotlight
justjanne May 30, 2022
d24efda
feat: clean up search hooks, remove unnecessary wrappers
justjanne May 31, 2022
9dacc50
feat: use variables for css where possible
justjanne May 31, 2022
fa6d722
comment: explain rationale for hiding threepid avatar from screenreaders
justjanne May 31, 2022
d5a5621
feat: use variables for css where possible
justjanne May 31, 2022
463217b
fix: correct bad automatic import
justjanne May 31, 2022
cbae360
fix: correct translation issue
justjanne May 31, 2022
d14bd3c
Merge remote-tracking branch 'origin/develop' into justjanne/feat/213…
justjanne May 31, 2022
8f4dd91
Merge remote-tracking branch 'origin/develop' into justjanne/feat/213…
justjanne Jun 1, 2022
5ef137c
fix: correct nullability issues
justjanne Jun 1, 2022
a99a4ab
feat: switch roomdirectory to use new network dropdown
justjanne Jun 1, 2022
975214b
feat: remove old network dropdown
justjanne Jun 1, 2022
2b2bb74
feat: replace old networkdropdown with new networkdropdown
justjanne Jun 1, 2022
c7a4242
feat: Rename generic dropdown menu
justjanne Jun 1, 2022
4d81f68
feat: improve styling of GenericDropdownMenu
justjanne Jun 1, 2022
4079af5
feat: improve styling of network dropdown
justjanne Jun 1, 2022
76728ca
feat: clamp length of topic for public room results
justjanne Jun 1, 2022
a3ab9d3
feat: update translations
justjanne Jun 1, 2022
e33d045
fix: correct lint issues
justjanne Jun 1, 2022
ea9bdfe
fix: implement changes requested by design
justjanne Jun 2, 2022
4d4d3f1
fix: update i18n
justjanne Jun 2, 2022
c68886e
Merge branch 'develop' into justjanne/feat/21354-search-directories
justjanne Jun 2, 2022
6c5c79b
Merge branch 'develop' into justjanne/feat/21354-search-directories
justjanne Jun 3, 2022
60dd35b
Merge branch 'develop' into justjanne/feat/21354-search-directories
justjanne Jun 3, 2022
5897a0a
test: add unit test for useDebouncedSearch
justjanne Jun 3, 2022
cc98b3f
test: add unit test for useProfileInfo
justjanne Jun 3, 2022
4558898
Merge branch 'develop' into justjanne/feat/21354-search-directories
justjanne Jun 7, 2022
cff9201
Merge branch 'develop' into justjanne/feat/21354-search-directories
justjanne Jun 7, 2022
9d38a1d
feat: allow setting initial filter
justjanne Jun 3, 2022
d481930
fix: avoid unnecessary lookup
justjanne Jun 3, 2022
3acde35
fix: solve potential bug
justjanne Jun 3, 2022
b6295f6
fix(test): correct typo in test name
justjanne Jun 7, 2022
1fba8fe
test: expand test cases
justjanne Jun 7, 2022
cfd091c
test: create test cases for spotlight dialog
justjanne Jun 7, 2022
eb2aff8
fix(test): correct issues found by tests
justjanne Jun 7, 2022
8db559a
fix: prevent the result of a slower request overriding the result of …
justjanne Jun 7, 2022
cb716a8
fix: implement spacing as requested by design
justjanne Jun 7, 2022
29fa8b2
fix: don't show space results with filter enabled
justjanne Jun 7, 2022
02a74c5
fix: clear people filter if query is empty
justjanne Jun 7, 2022
e7fa720
fix: avoid duplicate import
justjanne Jun 7, 2022
b2405b0
fix: correct eslint issue
justjanne Jun 7, 2022
6a79651
feat: show suggestions for people filter with empty query
justjanne Jun 8, 2022
e7fc1ba
feat: extract member sort algorithm from InviteDialog
justjanne Jun 8, 2022
33af6db
feat: sort people suggestions correctly
justjanne Jun 8, 2022
cf1e2c5
fix: improve comments
justjanne Jun 8, 2022
4a8bd53
Merge remote-tracking branch 'origin/develop' into justjanne/feat/213…
justjanne Jun 8, 2022
8ea4a46
feat: make useDebouncedCallback more generic
justjanne Jun 10, 2022
a7b1559
test: add test for useLatestResult to prevent out-of-order results
justjanne Jun 10, 2022
c1f6b6d
test: add utility helper to enable labs features
justjanne Jun 10, 2022
9374b6d
fix: prevent unnecessary duplicate requests
justjanne Jun 10, 2022
a177386
fix: correct overflow of result titles
justjanne Jun 10, 2022
2748e82
test: initial cypress test
justjanne Jun 10, 2022
5063a92
fix: make it easier for debounced callback to be used safely
justjanne Jun 10, 2022
ea85fd9
Merge remote-tracking branch 'origin/develop' into justjanne/feat/213…
justjanne Jun 10, 2022
e1979ba
fix(test): correct bad import
justjanne Jun 10, 2022
e0a16f6
fix(test): use bundle for import instead
justjanne Jun 10, 2022
6994470
Introduce test case for finding public rooms
justjanne Jun 13, 2022
fbb56f5
Fix issue where people filter could not be removed via keyboard
justjanne Jun 13, 2022
41f4100
Correctly import visibility
justjanne Jun 13, 2022
b54e6ad
Correctly test name
justjanne Jun 13, 2022
a202e3c
Fix cypress test imports
justjanne Jun 13, 2022
bc1963f
Test known and unknown rooms and users
justjanne Jun 13, 2022
2732442
Test navigating and activating results
justjanne Jun 13, 2022
cdd9507
Add Todo comments for tomorrow
justjanne Jun 13, 2022
7da28f0
Merge remote-tracking branch 'origin/develop' into justjanne/feat/213…
justjanne Jun 14, 2022
765e67c
Test search result destinations as well
justjanne Jun 14, 2022
e587cb3
Implement changes requested by design: "Other Searches" heading
justjanne Jun 14, 2022
2cb252b
Update i18n
justjanne Jun 14, 2022
8c3ed24
Add additional tests
justjanne Jun 14, 2022
4bdbd25
Make eslint happy
justjanne Jun 14, 2022
63629a6
Test selecting/removing filters via keyboard as well
justjanne Jun 14, 2022
f008bc0
Rename keyboard navigation test
justjanne Jun 14, 2022
5328bf1
Update spotlight test number
justjanne Jun 14, 2022
b9c69b7
Merge remote-tracking branch 'origin/develop' into justjanne/feat/213…
justjanne Jun 15, 2022
1ec0e04
Implement loading indicator, requested by design
justjanne Jun 15, 2022
54ea843
ci: empty commit to force CI to run again
justjanne Jun 15, 2022
646ab8a
Fix issue that was responsible for flaky tests
justjanne Jun 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
302 changes: 302 additions & 0 deletions cypress/integration/12-spotlight/spotlight.spec.ts
Original file line number Diff line number Diff line change
@@ -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");
});
});
});
3 changes: 2 additions & 1 deletion cypress/integration/5-threads/threads.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ describe("Threads", () => {
let synapse: SynapseInstance;

beforeEach(() => {
// Default threads to ON for this spec
cy.enableLabsFeature("feature_thread");
cy.window().then(win => {
win.localStorage.setItem("mx_lhs_size", "0"); // Collapse left panel for these tests
win.localStorage.setItem("mx_labs_feature_feature_thread", "true"); // Default threads to ON for this spec
});
cy.startSynapse("default").then(data => {
synapse = data;
Expand Down
1 change: 1 addition & 0 deletions cypress/support/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import "cypress-real-events";
import "./performance";
import "./synapse";
import "./login";
import "./labs";
import "./client";
import "./settings";
import "./bot";
Expand Down
42 changes: 42 additions & 0 deletions cypress/support/labs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
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 Chainable = Cypress.Chainable;

/// <reference types="cypress" />

declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Cypress {
interface Chainable {
/**
* 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")
*/
enableLabsFeature(feature: string): Chainable<null>;
}
}
}

Cypress.Commands.add("enableLabsFeature", (feature: string): Chainable<null> => {
return cy.window({ log: false }).then(win => {
win.localStorage.setItem(`mx_labs_feature_${feature}`, "true");
}).then(() => null);
});

// Needed to make this file a module
export { };
1 change: 1 addition & 0 deletions res/css/_components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
@import "./structures/_FileDropTarget.scss";
@import "./structures/_FilePanel.scss";
@import "./structures/_GenericErrorPage.scss";
@import "./structures/_GenericDropdownMenu.scss";
@import "./structures/_HeaderButtons.scss";
@import "./structures/_HomePage.scss";
@import "./structures/_LeftPanel.scss";
Expand Down
Loading