From 52e89cff6ca809ccf97a7abc19d7a6169d5860fa Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 11 Aug 2022 15:39:12 -0600 Subject: [PATCH 1/2] 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". --- 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 fee1e390713..63a51d83d9b 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 not regress by showing the same person result multiple times", () => { + 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 dd2b9232111..3d8b16c1f4f 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)); }, From 2ecec152a0b584c89e8bff4619764e3ba5fc5bab Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 11 Aug 2022 15:52:09 -0600 Subject: [PATCH 2/2] Update cypress/e2e/spotlight/spotlight.spec.ts Co-authored-by: Robin --- cypress/e2e/spotlight/spotlight.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/e2e/spotlight/spotlight.spec.ts b/cypress/e2e/spotlight/spotlight.spec.ts index 63a51d83d9b..63b3d00e1bc 100644 --- a/cypress/e2e/spotlight/spotlight.spec.ts +++ b/cypress/e2e/spotlight/spotlight.spec.ts @@ -376,7 +376,7 @@ describe("Spotlight", () => { }); // Test against https://github.com/vector-im/element-web/issues/22851 - it("should not regress by showing the same person result multiple times", () => { + it("should show each person result only once", () => { cy.openSpotlightDialog().within(() => { cy.spotlightFilter(Filter.People);