From 8db7766a4012f6ffa0707795698513c585ddaee1 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 11 Aug 2022 16:26:25 -0600 Subject: [PATCH] Add safety to the spotlight search dialog (#9177) * Add safety to the spotlight search dialog Fixes https://github.com/vector-im/element-web/issues/22851 This test was triggering the mentioned bug only occasionally because it was dependent on when the search settled: if it settled early then the length was wrong. In testing, the dialog was caught multiple times to have passed the length chat but update to show duplicated results before the test closed the client, indicating a race condition within the tests. To fix this, we just make sure everything settles before moving on. We do this on unaffected tests too to ensure they don't regress later. The affected test was "should find group DMs by usernames or user ids". * Update cypress/e2e/spotlight/spotlight.spec.ts Co-authored-by: Robin Co-authored-by: Robin --- cypress/e2e/spotlight/spotlight.spec.ts | 32 +++++++++++++++++++ .../dialogs/spotlight/SpotlightDialog.tsx | 4 ++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/cypress/e2e/spotlight/spotlight.spec.ts b/cypress/e2e/spotlight/spotlight.spec.ts index fee1e39071..63b3d00e1b 100644 --- a/cypress/e2e/spotlight/spotlight.spec.ts +++ b/cypress/e2e/spotlight/spotlight.spec.ts @@ -114,6 +114,7 @@ Cypress.Commands.add("startDM", (name: string) => { cy.openSpotlightDialog().within(() => { cy.spotlightFilter(Filter.People); cy.spotlightSearch().clear().type(name); + cy.wait(1000); // wait for the dialog code to settle cy.get(".mx_Spinner").should("not.exist"); cy.spotlightResults().should("have.length", 1); cy.spotlightResults().eq(0).should("contain", name); @@ -216,6 +217,7 @@ describe("Spotlight", () => { it("should find joined rooms", () => { cy.openSpotlightDialog().within(() => { cy.spotlightSearch().clear().type(room1Name); + cy.wait(1000); // wait for the dialog code to settle cy.spotlightResults().should("have.length", 1); cy.spotlightResults().eq(0).should("contain", room1Name); cy.spotlightResults().eq(0).click(); @@ -229,6 +231,7 @@ describe("Spotlight", () => { cy.openSpotlightDialog().within(() => { cy.spotlightFilter(Filter.PublicRooms); cy.spotlightSearch().clear().type(room1Name); + cy.wait(1000); // wait for the dialog code to settle cy.spotlightResults().should("have.length", 1); cy.spotlightResults().eq(0).should("contain", room1Name); cy.spotlightResults().eq(0).should("contain", "View"); @@ -243,6 +246,7 @@ describe("Spotlight", () => { cy.openSpotlightDialog().within(() => { cy.spotlightFilter(Filter.PublicRooms); cy.spotlightSearch().clear().type(room2Name); + cy.wait(1000); // wait for the dialog code to settle cy.spotlightResults().should("have.length", 1); cy.spotlightResults().eq(0).should("contain", room2Name); cy.spotlightResults().eq(0).should("contain", "Join"); @@ -258,6 +262,7 @@ describe("Spotlight", () => { cy.openSpotlightDialog().within(() => { cy.spotlightFilter(Filter.PublicRooms); cy.spotlightSearch().clear().type(room3Name); + cy.wait(1000); // wait for the dialog code to settle cy.spotlightResults().should("have.length", 1); cy.spotlightResults().eq(0).should("contain", room3Name); cy.spotlightResults().eq(0).should("contain", "View"); @@ -296,6 +301,7 @@ describe("Spotlight", () => { cy.openSpotlightDialog().within(() => { cy.spotlightFilter(Filter.People); cy.spotlightSearch().clear().type(bot1Name); + cy.wait(1000); // wait for the dialog code to settle cy.spotlightResults().should("have.length", 1); cy.spotlightResults().eq(0).should("contain", bot1Name); cy.spotlightResults().eq(0).click(); @@ -308,6 +314,7 @@ describe("Spotlight", () => { cy.openSpotlightDialog().within(() => { cy.spotlightFilter(Filter.People); cy.spotlightSearch().clear().type(bot2Name); + cy.wait(1000); // wait for the dialog code to settle cy.spotlightResults().should("have.length", 1); cy.spotlightResults().eq(0).should("contain", bot2Name); cy.spotlightResults().eq(0).click(); @@ -324,6 +331,7 @@ describe("Spotlight", () => { cy.openSpotlightDialog().within(() => { cy.spotlightFilter(Filter.People); cy.spotlightSearch().clear().type(bot2Name); + cy.wait(1000); // wait for the dialog code to settle cy.spotlightResults().should("have.length", 1); cy.spotlightResults().eq(0).should("contain", bot2Name); cy.spotlightResults().eq(0).click(); @@ -352,6 +360,7 @@ describe("Spotlight", () => { cy.openSpotlightDialog().within(() => { cy.spotlightFilter(Filter.People); cy.spotlightSearch().clear().type(bot1.getUserId()); + cy.wait(1000); // wait for the dialog code to settle cy.spotlightResults().should("have.length", 2); cy.spotlightResults().eq(0).should("contain", `${bot1Name} and ${bot2Name}`); }); @@ -360,15 +369,37 @@ describe("Spotlight", () => { cy.openSpotlightDialog().within(() => { cy.spotlightFilter(Filter.People); cy.spotlightSearch().clear().type(bot2.getUserId()); + cy.wait(1000); // wait for the dialog code to settle cy.spotlightResults().should("have.length", 2); cy.spotlightResults().eq(0).should("contain", `${bot1Name} and ${bot2Name}`); }); }); + // Test against https://github.com/vector-im/element-web/issues/22851 + it("should show each person result only once", () => { + cy.openSpotlightDialog().within(() => { + cy.spotlightFilter(Filter.People); + + // 2 rounds of search to simulate the bug conditions. Specifically, the first search + // should have 1 result (not 2) and the second search should also have 1 result (instead + // of the super buggy 3 described by https://github.com/vector-im/element-web/issues/22851) + // + // We search for user ID to trigger the profile lookup within the dialog. + for (let i = 0; i < 2; i++) { + cy.log("Iteration: " + i); + cy.spotlightSearch().clear().type(bot1.getUserId()); + cy.wait(1000); // wait for the dialog code to settle + cy.spotlightResults().should("have.length", 1); + cy.spotlightResults().eq(0).should("contain", bot1.getUserId()); + } + }); + }); + it("should allow opening group chat dialog", () => { cy.openSpotlightDialog().within(() => { cy.spotlightFilter(Filter.People); cy.spotlightSearch().clear().type(bot2Name); + cy.wait(1000); // wait for the dialog code to settle cy.spotlightResults().should("have.length", 1); cy.spotlightResults().eq(0).should("contain", bot2Name); cy.get(".mx_SpotlightDialog_startGroupChat").should("contain", "Start a group chat"); @@ -390,6 +421,7 @@ describe("Spotlight", () => { cy.openSpotlightDialog().within(() => { cy.spotlightFilter(Filter.People); cy.spotlightSearch().clear().type(bot1Name); + cy.wait(1000); // wait for the dialog code to settle cy.get(".mx_Spinner").should("not.exist"); cy.spotlightResults().should("have.length", 1); }); diff --git a/src/components/views/dialogs/spotlight/SpotlightDialog.tsx b/src/components/views/dialogs/spotlight/SpotlightDialog.tsx index dd2b923211..3d8b16c1f4 100644 --- a/src/components/views/dialogs/spotlight/SpotlightDialog.tsx +++ b/src/components/views/dialogs/spotlight/SpotlightDialog.tsx @@ -376,7 +376,9 @@ const SpotlightDialog: React.FC = ({ initialText = "", initialFilter = n })), ...roomResults, ...userResults, - ...(profile ? [new DirectoryMember(profile)] : []).map(toMemberResult), + ...(profile && !alreadyAddedUserIds.has(profile.user_id) + ? [new DirectoryMember(profile)] + : []).map(toMemberResult), ...publicRooms.map(toPublicRoomResult), ].filter(result => filter === null || result.filter.includes(filter)); },