Upgrade to Cypress 10 (#9008)
* Upgrade to Cypress 10 * Remove stale comment
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
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 { SynapseInstance } from "../../plugins/synapsedocker";
|
||||
|
||||
describe("Registration", () => {
|
||||
let synapse: SynapseInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.visit("/#/register");
|
||||
cy.startSynapse("consent").then(data => {
|
||||
synapse = data;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopSynapse(synapse);
|
||||
});
|
||||
|
||||
it("registers an account and lands on the home screen", () => {
|
||||
cy.get(".mx_ServerPicker_change", { timeout: 15000 }).click();
|
||||
cy.get(".mx_ServerPickerDialog_continue").should("be.visible");
|
||||
cy.percySnapshot("Server Picker");
|
||||
|
||||
cy.get(".mx_ServerPickerDialog_otherHomeserver").type(synapse.baseUrl);
|
||||
cy.get(".mx_ServerPickerDialog_continue").click();
|
||||
// wait for the dialog to go away
|
||||
cy.get('.mx_ServerPickerDialog').should('not.exist');
|
||||
|
||||
cy.get("#mx_RegistrationForm_username").should("be.visible");
|
||||
// Hide the server text as it contains the randomly allocated Synapse port
|
||||
const percyCSS = ".mx_ServerPicker_server { visibility: hidden !important; }";
|
||||
cy.percySnapshot("Registration", { percyCSS });
|
||||
|
||||
cy.get("#mx_RegistrationForm_username").type("alice");
|
||||
cy.get("#mx_RegistrationForm_password").type("totally a great password");
|
||||
cy.get("#mx_RegistrationForm_passwordConfirm").type("totally a great password");
|
||||
cy.startMeasuring("create-account");
|
||||
cy.get(".mx_Login_submit").click();
|
||||
|
||||
cy.get(".mx_RegistrationEmailPromptDialog").should("be.visible");
|
||||
cy.percySnapshot("Registration email prompt", { percyCSS });
|
||||
cy.get(".mx_RegistrationEmailPromptDialog button.mx_Dialog_primary").click();
|
||||
|
||||
cy.stopMeasuring("create-account");
|
||||
cy.get(".mx_InteractiveAuthEntryComponents_termsPolicy").should("be.visible");
|
||||
cy.percySnapshot("Registration terms prompt", { percyCSS });
|
||||
|
||||
cy.get(".mx_InteractiveAuthEntryComponents_termsPolicy input").click();
|
||||
cy.startMeasuring("from-submit-to-home");
|
||||
cy.get(".mx_InteractiveAuthEntryComponents_termsSubmit").click();
|
||||
|
||||
cy.url().should('contain', '/#/home');
|
||||
cy.stopMeasuring("from-submit-to-home");
|
||||
|
||||
cy.get('[aria-label="User menu"]').click();
|
||||
cy.get('[aria-label="Security & Privacy"]').click();
|
||||
cy.get(".mx_DevicesPanel_myDevice .mx_DevicesPanel_deviceTrust .mx_E2EIcon")
|
||||
.should("have.class", "mx_E2EIcon_verified");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
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 { SynapseInstance } from "../../plugins/synapsedocker";
|
||||
import { MatrixClient } from "../../global";
|
||||
|
||||
describe("UserView", () => {
|
||||
let synapse: SynapseInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.startSynapse("default").then(data => {
|
||||
synapse = data;
|
||||
|
||||
cy.initTestUser(synapse, "Violet");
|
||||
cy.getBot(synapse, { displayName: "Usman" }).as("bot");
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopSynapse(synapse);
|
||||
});
|
||||
|
||||
it("should render the user view as expected", () => {
|
||||
cy.get<MatrixClient>("@bot").then(bot => {
|
||||
cy.visit(`/#/user/${bot.getUserId()}`);
|
||||
});
|
||||
|
||||
cy.get("#mx_RightPanel .mx_UserInfo_profile h2").should("contain", "Usman");
|
||||
cy.get(".mx_RightPanel .mx_Spinner").should("not.exist"); // wait for spinners to finish
|
||||
cy.get(".mx_RightPanel").percySnapshotElement("User View", {
|
||||
// Hide the MXID field as it'll vary on each test
|
||||
percyCSS: ".mx_UserInfo_profile_mxid { visibility: hidden !important; }",
|
||||
widths: [260, 500],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
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, { displayName: "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<MatrixClient>("@bot"),
|
||||
cy.get<string>("@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<MatrixClient>("@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`);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
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>>;
|
||||
startDM(name: string): Chainable<void>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("startDM", (name: string) => {
|
||||
cy.openSpotlightDialog().within(() => {
|
||||
cy.spotlightFilter(Filter.People);
|
||||
cy.spotlightSearch().clear().type(name);
|
||||
cy.get(".mx_Spinner").should("not.exist");
|
||||
cy.spotlightResults().should("have.length", 1);
|
||||
cy.spotlightResults().eq(0).should("contain", name);
|
||||
cy.spotlightResults().eq(0).click();
|
||||
}).then(() => {
|
||||
cy.roomHeaderName().should("contain", name);
|
||||
cy.get(".mx_RoomSublist[aria-label=People]").should("contain", name);
|
||||
});
|
||||
});
|
||||
|
||||
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.startSynapse("default").then(data => {
|
||||
synapse = data;
|
||||
cy.initTestUser(synapse, "Jim").then(() =>
|
||||
cy.getBot(synapse, { displayName: bot1Name }).then(_bot1 => {
|
||||
bot1 = _bot1;
|
||||
}),
|
||||
).then(() =>
|
||||
cy.getBot(synapse, { displayName: 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.visit("/#/home");
|
||||
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 find group DMs by usernames or user ids", () => {
|
||||
// First we want to share a room with both bots to ensure we’ve got their usernames cached
|
||||
cy.inviteUser(room1Id, bot2.getUserId());
|
||||
|
||||
// Starting a DM with ByteBot (will be turned into a group dm later)
|
||||
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);
|
||||
cy.get(".mx_RoomSublist[aria-label=People]").should("contain", bot2Name);
|
||||
});
|
||||
|
||||
// Invite BotBob into existing DM with ByteBot
|
||||
cy.getDmRooms(bot2.getUserId()).then(dmRooms => dmRooms[0])
|
||||
.then(groupDmId => cy.inviteUser(groupDmId, bot1.getUserId()))
|
||||
.then(() => {
|
||||
cy.roomHeaderName().should("contain", `${bot1Name} and ${bot2Name}`);
|
||||
cy.get(".mx_RoomSublist[aria-label=People]").should("contain", `${bot1Name} and ${bot2Name}`);
|
||||
});
|
||||
|
||||
// Search for BotBob by id, should return group DM and user
|
||||
cy.openSpotlightDialog().within(() => {
|
||||
cy.spotlightFilter(Filter.People);
|
||||
cy.spotlightSearch().clear().type(bot1.getUserId());
|
||||
cy.spotlightResults().should("have.length", 2);
|
||||
cy.spotlightResults().eq(0).should("contain", `${bot1Name} and ${bot2Name}`);
|
||||
});
|
||||
|
||||
// Search for ByteBot by id, should return group DM and user
|
||||
cy.openSpotlightDialog().within(() => {
|
||||
cy.spotlightFilter(Filter.People);
|
||||
cy.spotlightSearch().clear().type(bot2.getUserId());
|
||||
cy.spotlightResults().should("have.length", 2);
|
||||
cy.spotlightResults().eq(0).should("contain", `${bot1Name} and ${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 close spotlight after starting a DM", () => {
|
||||
cy.startDM(bot1Name);
|
||||
cy.get(".mx_SpotlightDialog").should("have.length", 0);
|
||||
});
|
||||
|
||||
it("should show the same user only once", () => {
|
||||
cy.startDM(bot1Name);
|
||||
cy.visit("/#/home");
|
||||
|
||||
cy.openSpotlightDialog().within(() => {
|
||||
cy.spotlightFilter(Filter.People);
|
||||
cy.spotlightSearch().clear().type(bot1Name);
|
||||
cy.get(".mx_Spinner").should("not.exist");
|
||||
cy.spotlightResults().should("have.length", 1);
|
||||
});
|
||||
});
|
||||
|
||||
it("should be able to navigate results via keyboard", () => {
|
||||
cy.openSpotlightDialog().within(() => {
|
||||
cy.spotlightFilter(Filter.People);
|
||||
cy.spotlightSearch().clear().type("b");
|
||||
// our debouncing logic only starts the search after a short timeout,
|
||||
// so we wait a few milliseconds.
|
||||
cy.wait(300);
|
||||
cy.get(".mx_Spinner").should("not.exist").then(() => {
|
||||
cy.spotlightResults().should("have.length", 2).then(() => {
|
||||
cy.spotlightResults().eq(0)
|
||||
.should("have.attr", "aria-selected", "true");
|
||||
cy.spotlightResults().eq(1)
|
||||
.should("have.attr", "aria-selected", "false");
|
||||
});
|
||||
cy.spotlightSearch().type("{downArrow}").then(() => {
|
||||
cy.spotlightResults().eq(0)
|
||||
.should("have.attr", "aria-selected", "false");
|
||||
cy.spotlightResults().eq(1)
|
||||
.should("have.attr", "aria-selected", "true");
|
||||
});
|
||||
cy.spotlightSearch().type("{downArrow}").then(() => {
|
||||
cy.spotlightResults().eq(0)
|
||||
.should("have.attr", "aria-selected", "false");
|
||||
cy.spotlightResults().eq(1)
|
||||
.should("have.attr", "aria-selected", "false");
|
||||
});
|
||||
cy.spotlightSearch().type("{upArrow}").then(() => {
|
||||
cy.spotlightResults().eq(0)
|
||||
.should("have.attr", "aria-selected", "false");
|
||||
cy.spotlightResults().eq(1)
|
||||
.should("have.attr", "aria-selected", "true");
|
||||
});
|
||||
cy.spotlightSearch().type("{upArrow}").then(() => {
|
||||
cy.spotlightResults().eq(0)
|
||||
.should("have.attr", "aria-selected", "true");
|
||||
cy.spotlightResults().eq(1)
|
||||
.should("have.attr", "aria-selected", "false");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
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 { SynapseInstance } from "../../plugins/synapsedocker";
|
||||
|
||||
describe("Pills", () => {
|
||||
let synapse: SynapseInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.startSynapse("default").then(data => {
|
||||
synapse = data;
|
||||
|
||||
cy.initTestUser(synapse, "Sally");
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopSynapse(synapse);
|
||||
});
|
||||
|
||||
it('should navigate clicks internally to the app', () => {
|
||||
const messageRoom = "Send Messages Here";
|
||||
const targetLocalpart = "aliasssssssssssss";
|
||||
cy.createRoom({
|
||||
name: "Target",
|
||||
room_alias_name: targetLocalpart,
|
||||
}).as("targetRoomId");
|
||||
cy.createRoom({
|
||||
name: messageRoom,
|
||||
}).as("messageRoomId");
|
||||
cy.all([
|
||||
cy.get<string>("@targetRoomId"),
|
||||
cy.get<string>("@messageRoomId"),
|
||||
]).then(([targetRoomId, messageRoomId]) => { // discard the target room ID - we don't need it
|
||||
cy.viewRoomByName(messageRoom);
|
||||
cy.url().should("contain", `/#/room/${messageRoomId}`);
|
||||
|
||||
// send a message using the built-in room mention functionality (autocomplete)
|
||||
cy.get(".mx_SendMessageComposer .mx_BasicMessageComposer_input")
|
||||
.type(`Hello world! Join here: #${targetLocalpart.substring(0, 3)}`);
|
||||
cy.get(".mx_Autocomplete_Completion_title").click();
|
||||
cy.get(".mx_MessageComposer_sendMessage").click();
|
||||
|
||||
// find the pill in the timeline and click it
|
||||
cy.get(".mx_EventTile_body .mx_Pill").click();
|
||||
|
||||
// verify we landed at a sane place
|
||||
cy.url().should("contain", `/#/room/#${targetLocalpart}:`);
|
||||
|
||||
cy.wait(250); // let the room list settle
|
||||
|
||||
// go back to the message room and try to click on the pill text, as a user would
|
||||
cy.viewRoomByName(messageRoom);
|
||||
cy.get(".mx_EventTile_body .mx_Pill .mx_Pill_linkText")
|
||||
.should("have.css", "pointer-events", "none")
|
||||
.click({ force: true }); // force is to ensure we bypass pointer-events
|
||||
cy.url().should("contain", `https://matrix.to/#/#${targetLocalpart}:`);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
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 { SinonStub } from "cypress/types/sinon";
|
||||
|
||||
import { SynapseInstance } from "../../plugins/synapsedocker";
|
||||
|
||||
describe("Consent", () => {
|
||||
let synapse: SynapseInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.startSynapse("consent").then(data => {
|
||||
synapse = data;
|
||||
|
||||
cy.initTestUser(synapse, "Bob");
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopSynapse(synapse);
|
||||
});
|
||||
|
||||
it("should prompt the user to consent to terms when server deems it necessary", () => {
|
||||
// Attempt to create a room using the js-sdk which should return an error with `M_CONSENT_NOT_GIVEN`
|
||||
cy.window().then(win => {
|
||||
win.mxMatrixClientPeg.matrixClient.createRoom({}).catch(() => {});
|
||||
|
||||
// Stub `window.open` - clicking the primary button below will call it
|
||||
cy.stub(win, "open").as("windowOpen").returns({});
|
||||
});
|
||||
|
||||
// Accept terms & conditions
|
||||
cy.get(".mx_QuestionDialog").within(() => {
|
||||
cy.get("#mx_BaseDialog_title").contains("Terms and Conditions");
|
||||
cy.get(".mx_Dialog_primary").click();
|
||||
});
|
||||
|
||||
cy.get<SinonStub>("@windowOpen").then(stub => {
|
||||
const url = stub.getCall(0).args[0];
|
||||
|
||||
// Go to Synapse's consent page and accept it
|
||||
cy.origin(synapse.baseUrl, { args: { url } }, ({ url }) => {
|
||||
cy.visit(url);
|
||||
|
||||
cy.get('[type="submit"]').click();
|
||||
cy.get("p").contains("Danke schon");
|
||||
});
|
||||
});
|
||||
|
||||
// go back to the app
|
||||
cy.visit("/");
|
||||
// wait for the app to re-load
|
||||
cy.get(".mx_MatrixChat", { timeout: 15000 });
|
||||
|
||||
// attempt to perform the same action again and expect it to not fail
|
||||
cy.createRoom({});
|
||||
});
|
||||
});
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
import { SynapseInstance } from "../../plugins/synapsedocker";
|
||||
|
||||
describe("Login", () => {
|
||||
let synapse: SynapseInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.visit("/#/login");
|
||||
cy.startSynapse("consent").then(data => {
|
||||
synapse = data;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopSynapse(synapse);
|
||||
});
|
||||
|
||||
describe("m.login.password", () => {
|
||||
const username = "user1234";
|
||||
const password = "p4s5W0rD";
|
||||
|
||||
beforeEach(() => {
|
||||
cy.registerUser(synapse, username, password);
|
||||
});
|
||||
|
||||
it("logs in with an existing account and lands on the home screen", () => {
|
||||
cy.get("#mx_LoginForm_username", { timeout: 15000 }).should("be.visible");
|
||||
cy.percySnapshot("Login");
|
||||
|
||||
cy.get(".mx_ServerPicker_change").click();
|
||||
cy.get(".mx_ServerPickerDialog_otherHomeserver").type(synapse.baseUrl);
|
||||
cy.get(".mx_ServerPickerDialog_continue").click();
|
||||
// wait for the dialog to go away
|
||||
cy.get('.mx_ServerPickerDialog').should('not.exist');
|
||||
|
||||
cy.get("#mx_LoginForm_username").type(username);
|
||||
cy.get("#mx_LoginForm_password").type(password);
|
||||
cy.startMeasuring("from-submit-to-home");
|
||||
cy.get(".mx_Login_submit").click();
|
||||
|
||||
cy.url().should('contain', '/#/home');
|
||||
cy.stopMeasuring("from-submit-to-home");
|
||||
});
|
||||
});
|
||||
|
||||
describe("logout", () => {
|
||||
beforeEach(() => {
|
||||
cy.initTestUser(synapse, "Erin");
|
||||
});
|
||||
|
||||
it("should go to login page on logout", () => {
|
||||
cy.get('[aria-label="User menu"]').click();
|
||||
|
||||
// give a change for the outstanding requests queue to settle before logging out
|
||||
cy.wait(500);
|
||||
|
||||
cy.get(".mx_UserMenu_contextMenu").within(() => {
|
||||
cy.get(".mx_UserMenu_iconSignOut").click();
|
||||
});
|
||||
|
||||
cy.url().should("contain", "/#/login");
|
||||
});
|
||||
|
||||
it("should respect logout_redirect_url", () => {
|
||||
cy.tweakConfig({
|
||||
// We redirect to decoder-ring because it's a predictable page that isn't Element itself.
|
||||
// We could use example.org, matrix.org, or something else, however this puts dependency of external
|
||||
// infrastructure on our tests. In the same vein, we don't really want to figure out how to ship a
|
||||
// `test-landing.html` page when running with an uncontrolled Element (via `yarn start`).
|
||||
// Using the decoder-ring is just as fine, and we can search for strategic names.
|
||||
logout_redirect_url: "/decoder-ring/",
|
||||
});
|
||||
|
||||
cy.get('[aria-label="User menu"]').click();
|
||||
|
||||
// give a change for the outstanding requests queue to settle before logging out
|
||||
cy.wait(500);
|
||||
|
||||
cy.get(".mx_UserMenu_contextMenu").within(() => {
|
||||
cy.get(".mx_UserMenu_iconSignOut").click();
|
||||
});
|
||||
|
||||
cy.url().should("contains", "decoder-ring");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
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 { SynapseInstance } from "../../plugins/synapsedocker";
|
||||
import type { UserCredentials } from "../../support/login";
|
||||
|
||||
describe("User Menu", () => {
|
||||
let synapse: SynapseInstance;
|
||||
let user: UserCredentials;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.startSynapse("default").then(data => {
|
||||
synapse = data;
|
||||
|
||||
cy.initTestUser(synapse, "Jeff").then(credentials => {
|
||||
user = credentials;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopSynapse(synapse);
|
||||
});
|
||||
|
||||
it("should contain our name & userId", () => {
|
||||
cy.get('[aria-label="User menu"]').click();
|
||||
cy.get(".mx_UserMenu_contextMenu").within(() => {
|
||||
cy.get(".mx_UserMenu_contextMenu_displayName").should("contain", "Jeff");
|
||||
cy.get(".mx_UserMenu_contextMenu_userId").should("contain", user.userId);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
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 { SynapseInstance } from "../../plugins/synapsedocker";
|
||||
import Chainable = Cypress.Chainable;
|
||||
|
||||
function openCreateRoomDialog(): Chainable<JQuery<HTMLElement>> {
|
||||
cy.get('[aria-label="Add room"]').click();
|
||||
cy.get('.mx_ContextualMenu [aria-label="New room"]').click();
|
||||
return cy.get(".mx_CreateRoomDialog");
|
||||
}
|
||||
|
||||
describe("Create Room", () => {
|
||||
let synapse: SynapseInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.startSynapse("default").then(data => {
|
||||
synapse = data;
|
||||
|
||||
cy.initTestUser(synapse, "Jim");
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopSynapse(synapse);
|
||||
});
|
||||
|
||||
it("should allow us to create a public room with name, topic & address set", () => {
|
||||
const name = "Test room 1";
|
||||
const topic = "This room is dedicated to this test and this test only!";
|
||||
|
||||
openCreateRoomDialog().within(() => {
|
||||
// Fill name & topic
|
||||
cy.get('[label="Name"]').type(name);
|
||||
cy.get('[label="Topic (optional)"]').type(topic);
|
||||
// Change room to public
|
||||
cy.get('[aria-label="Room visibility"]').click();
|
||||
cy.get("#mx_JoinRuleDropdown__public").click();
|
||||
// Fill room address
|
||||
cy.get('[label="Room address"]').type("test-room-1");
|
||||
// Submit
|
||||
cy.startMeasuring("from-submit-to-room");
|
||||
cy.get(".mx_Dialog_primary").click();
|
||||
});
|
||||
|
||||
cy.url().should("contain", "/#/room/#test-room-1:localhost");
|
||||
cy.stopMeasuring("from-submit-to-room");
|
||||
cy.get(".mx_RoomHeader_nametext").contains(name);
|
||||
cy.get(".mx_RoomHeader_topic").contains(topic);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
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 { SynapseInstance } from "../../plugins/synapsedocker";
|
||||
import { MatrixClient } from "../../global";
|
||||
|
||||
function markWindowBeforeReload(): void {
|
||||
// mark our window object to "know" when it gets reloaded
|
||||
cy.window().then(w => w.beforeReload = true);
|
||||
}
|
||||
|
||||
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
|
||||
});
|
||||
cy.startSynapse("default").then(data => {
|
||||
synapse = data;
|
||||
|
||||
cy.initTestUser(synapse, "Tom");
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopSynapse(synapse);
|
||||
});
|
||||
|
||||
it("should reload when enabling threads beta", () => {
|
||||
markWindowBeforeReload();
|
||||
|
||||
// Turn off
|
||||
cy.openUserSettings("Labs").within(() => {
|
||||
// initially the new property is there
|
||||
cy.window().should("have.prop", "beforeReload", true);
|
||||
|
||||
cy.leaveBeta("Threads");
|
||||
// after reload the property should be gone
|
||||
cy.window().should("not.have.prop", "beforeReload");
|
||||
});
|
||||
|
||||
cy.get(".mx_MatrixChat", { timeout: 15000 }); // wait for the app
|
||||
markWindowBeforeReload();
|
||||
|
||||
// Turn on
|
||||
cy.openUserSettings("Labs").within(() => {
|
||||
// initially the new property is there
|
||||
cy.window().should("have.prop", "beforeReload", true);
|
||||
|
||||
cy.joinBeta("Threads");
|
||||
// after reload the property should be gone
|
||||
cy.window().should("not.have.prop", "beforeReload");
|
||||
});
|
||||
});
|
||||
|
||||
it("should be usable for a conversation", () => {
|
||||
let bot: MatrixClient;
|
||||
cy.getBot(synapse, { displayName: "BotBob" }).then(_bot => {
|
||||
bot = _bot;
|
||||
});
|
||||
|
||||
let roomId: string;
|
||||
cy.createRoom({}).then(_roomId => {
|
||||
roomId = _roomId;
|
||||
cy.inviteUser(roomId, bot.getUserId());
|
||||
cy.visit("/#/room/" + roomId);
|
||||
});
|
||||
|
||||
// User sends message
|
||||
cy.get(".mx_RoomView_body .mx_BasicMessageComposer_input").type("Hello Mr. Bot{enter}");
|
||||
|
||||
// Wait for message to send, get its ID and save as @threadId
|
||||
cy.get(".mx_RoomView_body .mx_EventTile").contains(".mx_EventTile[data-scroll-tokens]", "Hello Mr. Bot")
|
||||
.invoke("attr", "data-scroll-tokens").as("threadId");
|
||||
|
||||
// Bot starts thread
|
||||
cy.get<string>("@threadId").then(threadId => {
|
||||
bot.sendMessage(roomId, threadId, {
|
||||
body: "Hello there",
|
||||
msgtype: "m.text",
|
||||
});
|
||||
});
|
||||
|
||||
// User asserts timeline thread summary visible & clicks it
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_sender").should("contain", "BotBob");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_content").should("contain", "Hello there");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary").click();
|
||||
|
||||
// User responds in thread
|
||||
cy.get(".mx_ThreadView .mx_BasicMessageComposer_input").type("Test{enter}");
|
||||
|
||||
// User asserts summary was updated correctly
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_sender").should("contain", "Tom");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_content").should("contain", "Test");
|
||||
|
||||
// User reacts to message instead
|
||||
cy.get(".mx_ThreadView .mx_EventTile").contains(".mx_EventTile_line", "Hello there")
|
||||
.find('[aria-label="React"]').click({ force: true }); // Cypress has no ability to hover
|
||||
cy.get(".mx_EmojiPicker").within(() => {
|
||||
cy.get('input[type="text"]').type("wave");
|
||||
cy.get('[role="menuitem"]').contains("👋").click();
|
||||
});
|
||||
|
||||
// User redacts their prior response
|
||||
cy.get(".mx_ThreadView .mx_EventTile").contains(".mx_EventTile_line", "Test")
|
||||
.find('[aria-label="Options"]').click({ force: true }); // Cypress has no ability to hover
|
||||
cy.get(".mx_IconizedContextMenu").within(() => {
|
||||
cy.get('[role="menuitem"]').contains("Remove").click();
|
||||
});
|
||||
cy.get(".mx_TextInputDialog").within(() => {
|
||||
cy.get(".mx_Dialog_primary").contains("Remove").click();
|
||||
});
|
||||
|
||||
// User asserts summary was updated correctly
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_sender").should("contain", "BotBob");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_content").should("contain", "Hello there");
|
||||
|
||||
// User closes right panel after clicking back to thread list
|
||||
cy.get(".mx_ThreadView .mx_BaseCard_back").click();
|
||||
cy.get(".mx_ThreadPanel .mx_BaseCard_close").click();
|
||||
|
||||
// Bot responds to thread
|
||||
cy.get<string>("@threadId").then(threadId => {
|
||||
bot.sendMessage(roomId, threadId, {
|
||||
body: "How are things?",
|
||||
msgtype: "m.text",
|
||||
});
|
||||
});
|
||||
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_sender").should("contain", "BotBob");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_content").should("contain", "How are things?");
|
||||
// User asserts thread list unread indicator
|
||||
cy.get('.mx_HeaderButtons [aria-label="Threads"]').should("have.class", "mx_RightPanel_headerButton_unread");
|
||||
|
||||
// User opens thread list
|
||||
cy.get('.mx_HeaderButtons [aria-label="Threads"]').click();
|
||||
|
||||
// User asserts thread with correct root & latest events & unread dot
|
||||
cy.get(".mx_ThreadPanel .mx_EventTile_last").within(() => {
|
||||
cy.get(".mx_EventTile_body").should("contain", "Hello Mr. Bot");
|
||||
cy.get(".mx_ThreadSummary_content").should("contain", "How are things?");
|
||||
// User opens thread via threads list
|
||||
cy.get(".mx_EventTile_line").click();
|
||||
});
|
||||
|
||||
// User responds & asserts
|
||||
cy.get(".mx_ThreadView .mx_BasicMessageComposer_input").type("Great!{enter}");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_sender").should("contain", "Tom");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_content").should("contain", "Great!");
|
||||
|
||||
// User edits & asserts
|
||||
cy.get(".mx_ThreadView .mx_EventTile_last").contains(".mx_EventTile_line", "Great!").within(() => {
|
||||
cy.get('[aria-label="Edit"]').click({ force: true }); // Cypress has no ability to hover
|
||||
cy.get(".mx_BasicMessageComposer_input").type(" How about yourself?{enter}");
|
||||
});
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_sender").should("contain", "Tom");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_content")
|
||||
.should("contain", "Great! How about yourself?");
|
||||
|
||||
// User closes right panel
|
||||
cy.get(".mx_ThreadView .mx_BaseCard_close").click();
|
||||
|
||||
// Bot responds to thread and saves the id of their message to @eventId
|
||||
cy.get<string>("@threadId").then(threadId => {
|
||||
cy.wrap(bot.sendMessage(roomId, threadId, {
|
||||
body: "I'm very good thanks",
|
||||
msgtype: "m.text",
|
||||
}).then(res => res.event_id)).as("eventId");
|
||||
});
|
||||
|
||||
// User asserts
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_sender").should("contain", "BotBob");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_content")
|
||||
.should("contain", "I'm very good thanks");
|
||||
|
||||
// Bot edits their latest event
|
||||
cy.get<string>("@eventId").then(eventId => {
|
||||
bot.sendMessage(roomId, {
|
||||
"body": "* I'm very good thanks :)",
|
||||
"msgtype": "m.text",
|
||||
"m.new_content": {
|
||||
"body": "I'm very good thanks :)",
|
||||
"msgtype": "m.text",
|
||||
},
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.replace",
|
||||
"event_id": eventId,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// User asserts
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_sender").should("contain", "BotBob");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_content")
|
||||
.should("contain", "I'm very good thanks :)");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
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 type { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import type { ICreateRoomOpts } from "matrix-js-sdk/src/@types/requests";
|
||||
import { SynapseInstance } from "../../plugins/synapsedocker";
|
||||
import Chainable = Cypress.Chainable;
|
||||
import { UserCredentials } from "../../support/login";
|
||||
|
||||
function openSpaceCreateMenu(): Chainable<JQuery> {
|
||||
cy.get(".mx_SpaceButton_new").click();
|
||||
return cy.get(".mx_SpaceCreateMenu_wrapper .mx_ContextualMenu");
|
||||
}
|
||||
|
||||
function getSpacePanelButton(spaceName: string): Chainable<JQuery> {
|
||||
return cy.get(`.mx_SpaceButton[aria-label="${spaceName}"]`);
|
||||
}
|
||||
|
||||
function openSpaceContextMenu(spaceName: string): Chainable<JQuery> {
|
||||
getSpacePanelButton(spaceName).rightclick();
|
||||
return cy.get(".mx_SpacePanel_contextMenu");
|
||||
}
|
||||
|
||||
function spaceCreateOptions(spaceName: string): ICreateRoomOpts {
|
||||
return {
|
||||
creation_content: {
|
||||
type: "m.space",
|
||||
},
|
||||
initial_state: [{
|
||||
type: "m.room.name",
|
||||
content: {
|
||||
name: spaceName,
|
||||
},
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
function spaceChildInitialState(roomId: string): ICreateRoomOpts["initial_state"]["0"] {
|
||||
return {
|
||||
type: "m.space.child",
|
||||
state_key: roomId,
|
||||
content: {
|
||||
via: [roomId.split(":")[1]],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe("Spaces", () => {
|
||||
let synapse: SynapseInstance;
|
||||
let user: UserCredentials;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.startSynapse("default").then(data => {
|
||||
synapse = data;
|
||||
|
||||
cy.initTestUser(synapse, "Sue").then(_user => {
|
||||
user = _user;
|
||||
cy.mockClipboard();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopSynapse(synapse);
|
||||
});
|
||||
|
||||
it("should allow user to create public space", () => {
|
||||
openSpaceCreateMenu().within(() => {
|
||||
cy.get(".mx_SpaceCreateMenuType_public").click();
|
||||
cy.get('.mx_SpaceBasicSettings_avatarContainer input[type="file"]')
|
||||
.selectFile("cypress/fixtures/riot.png", { force: true });
|
||||
cy.get('input[label="Name"]').type("Let's have a Riot");
|
||||
cy.get('input[label="Address"]').should("have.value", "lets-have-a-riot");
|
||||
cy.get('textarea[label="Description"]').type("This is a space to reminisce Riot.im!");
|
||||
cy.get(".mx_AccessibleButton").contains("Create").click();
|
||||
});
|
||||
|
||||
// Create the default General & Random rooms, as well as a custom "Jokes" room
|
||||
cy.get('input[label="Room name"][value="General"]').should("exist");
|
||||
cy.get('input[label="Room name"][value="Random"]').should("exist");
|
||||
cy.get('input[placeholder="Support"]').type("Jokes");
|
||||
cy.get(".mx_AccessibleButton").contains("Continue").click();
|
||||
|
||||
// Copy matrix.to link
|
||||
cy.get(".mx_SpacePublicShare_shareButton").focus().realClick();
|
||||
cy.getClipboardText().should("eq", "https://matrix.to/#/#lets-have-a-riot:localhost");
|
||||
|
||||
// Go to space home
|
||||
cy.get(".mx_AccessibleButton").contains("Go to my first room").click();
|
||||
|
||||
// Assert rooms exist in the room list
|
||||
cy.get(".mx_RoomTile").contains("General").should("exist");
|
||||
cy.get(".mx_RoomTile").contains("Random").should("exist");
|
||||
cy.get(".mx_RoomTile").contains("Jokes").should("exist");
|
||||
});
|
||||
|
||||
it("should allow user to create private space", () => {
|
||||
openSpaceCreateMenu().within(() => {
|
||||
cy.get(".mx_SpaceCreateMenuType_private").click();
|
||||
cy.get('.mx_SpaceBasicSettings_avatarContainer input[type="file"]')
|
||||
.selectFile("cypress/fixtures/riot.png", { force: true });
|
||||
cy.get('input[label="Name"]').type("This is not a Riot");
|
||||
cy.get('input[label="Address"]').should("not.exist");
|
||||
cy.get('textarea[label="Description"]').type("This is a private space of mourning Riot.im...");
|
||||
cy.get(".mx_AccessibleButton").contains("Create").click();
|
||||
});
|
||||
|
||||
cy.get(".mx_SpaceRoomView_privateScope_meAndMyTeammatesButton").click();
|
||||
|
||||
// Create the default General & Random rooms, as well as a custom "Projects" room
|
||||
cy.get('input[label="Room name"][value="General"]').should("exist");
|
||||
cy.get('input[label="Room name"][value="Random"]').should("exist");
|
||||
cy.get('input[placeholder="Support"]').type("Projects");
|
||||
cy.get(".mx_AccessibleButton").contains("Continue").click();
|
||||
|
||||
cy.get(".mx_SpaceRoomView").should("contain", "Invite your teammates");
|
||||
cy.get(".mx_AccessibleButton").contains("Skip for now").click();
|
||||
|
||||
// Assert rooms exist in the room list
|
||||
cy.get(".mx_RoomTile").contains("General").should("exist");
|
||||
cy.get(".mx_RoomTile").contains("Random").should("exist");
|
||||
cy.get(".mx_RoomTile").contains("Projects").should("exist");
|
||||
|
||||
// Assert rooms exist in the space explorer
|
||||
cy.get(".mx_SpaceHierarchy_roomTile").contains("General").should("exist");
|
||||
cy.get(".mx_SpaceHierarchy_roomTile").contains("Random").should("exist");
|
||||
cy.get(".mx_SpaceHierarchy_roomTile").contains("Projects").should("exist");
|
||||
});
|
||||
|
||||
it("should allow user to create just-me space", () => {
|
||||
cy.createRoom({
|
||||
name: "Sample Room",
|
||||
});
|
||||
|
||||
openSpaceCreateMenu().within(() => {
|
||||
cy.get(".mx_SpaceCreateMenuType_private").click();
|
||||
cy.get('.mx_SpaceBasicSettings_avatarContainer input[type="file"]')
|
||||
.selectFile("cypress/fixtures/riot.png", { force: true });
|
||||
cy.get('input[label="Address"]').should("not.exist");
|
||||
cy.get('textarea[label="Description"]').type("This is a personal space to mourn Riot.im...");
|
||||
cy.get('input[label="Name"]').type("This is my Riot{enter}");
|
||||
});
|
||||
|
||||
cy.get(".mx_SpaceRoomView_privateScope_justMeButton").click();
|
||||
|
||||
cy.get(".mx_AddExistingToSpace_entry").click();
|
||||
cy.get(".mx_AccessibleButton").contains("Add").click();
|
||||
|
||||
cy.get(".mx_RoomTile").contains("Sample Room").should("exist");
|
||||
cy.get(".mx_SpaceHierarchy_roomTile").contains("Sample Room").should("exist");
|
||||
});
|
||||
|
||||
it("should allow user to invite another to a space", () => {
|
||||
let bot: MatrixClient;
|
||||
cy.getBot(synapse, { displayName: "BotBob" }).then(_bot => {
|
||||
bot = _bot;
|
||||
});
|
||||
|
||||
cy.createSpace({
|
||||
visibility: "public" as any,
|
||||
room_alias_name: "space",
|
||||
}).as("spaceId");
|
||||
|
||||
openSpaceContextMenu("#space:localhost").within(() => {
|
||||
cy.get('.mx_SpacePanel_contextMenu_inviteButton[aria-label="Invite"]').click();
|
||||
});
|
||||
|
||||
cy.get(".mx_SpacePublicShare").within(() => {
|
||||
// Copy link first
|
||||
cy.get(".mx_SpacePublicShare_shareButton").focus().realClick();
|
||||
cy.getClipboardText().should("eq", "https://matrix.to/#/#space:localhost");
|
||||
// Start Matrix invite flow
|
||||
cy.get(".mx_SpacePublicShare_inviteButton").click();
|
||||
});
|
||||
|
||||
cy.get(".mx_InviteDialog_other").within(() => {
|
||||
cy.get('input[type="text"]').type(bot.getUserId());
|
||||
cy.get(".mx_AccessibleButton").contains("Invite").click();
|
||||
});
|
||||
|
||||
cy.get(".mx_InviteDialog_other").should("not.exist");
|
||||
});
|
||||
|
||||
it("should show space invites at the top of the space panel", () => {
|
||||
cy.createSpace({
|
||||
name: "My Space",
|
||||
});
|
||||
getSpacePanelButton("My Space").should("exist");
|
||||
|
||||
cy.getBot(synapse, { displayName: "BotBob" }).then({ timeout: 10000 }, async bot => {
|
||||
const { room_id: roomId } = await bot.createRoom(spaceCreateOptions("Space Space"));
|
||||
await bot.invite(roomId, user.userId);
|
||||
});
|
||||
// Assert that `Space Space` is above `My Space` due to it being an invite
|
||||
getSpacePanelButton("Space Space").should("exist")
|
||||
.parent().next().find('.mx_SpaceButton[aria-label="My Space"]').should("exist");
|
||||
});
|
||||
|
||||
it("should include rooms in space home", () => {
|
||||
cy.createRoom({
|
||||
name: "Music",
|
||||
}).as("roomId1");
|
||||
cy.createRoom({
|
||||
name: "Gaming",
|
||||
}).as("roomId2");
|
||||
|
||||
const spaceName = "Spacey Mc. Space Space";
|
||||
cy.all([
|
||||
cy.get<string>("@roomId1"),
|
||||
cy.get<string>("@roomId2"),
|
||||
]).then(([roomId1, roomId2]) => {
|
||||
cy.createSpace({
|
||||
name: spaceName,
|
||||
initial_state: [
|
||||
spaceChildInitialState(roomId1),
|
||||
spaceChildInitialState(roomId2),
|
||||
],
|
||||
}).as("spaceId");
|
||||
});
|
||||
|
||||
cy.get("@spaceId").then(() => {
|
||||
getSpacePanelButton(spaceName).dblclick(); // Open space home
|
||||
});
|
||||
cy.get(".mx_SpaceRoomView .mx_SpaceHierarchy_list").within(() => {
|
||||
cy.get(".mx_SpaceHierarchy_roomTile").contains("Music").should("exist");
|
||||
cy.get(".mx_SpaceHierarchy_roomTile").contains("Gaming").should("exist");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
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 type { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||
import type { ISasEvent } from "matrix-js-sdk/src/crypto/verification/SAS";
|
||||
import type { MatrixClient, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { SynapseInstance } from "../../plugins/synapsedocker";
|
||||
import Chainable = Cypress.Chainable;
|
||||
|
||||
type EmojiMapping = [emoji: string, name: string];
|
||||
interface CryptoTestContext extends Mocha.Context {
|
||||
synapse: SynapseInstance;
|
||||
bob: MatrixClient;
|
||||
}
|
||||
|
||||
const waitForVerificationRequest = (cli: MatrixClient): Promise<VerificationRequest> => {
|
||||
return new Promise<VerificationRequest>(resolve => {
|
||||
const onVerificationRequestEvent = (request: VerificationRequest) => {
|
||||
// @ts-ignore CryptoEvent is not exported to window.matrixcs; using the string value here
|
||||
cli.off("crypto.verification.request", onVerificationRequestEvent);
|
||||
resolve(request);
|
||||
};
|
||||
// @ts-ignore
|
||||
cli.on("crypto.verification.request", onVerificationRequestEvent);
|
||||
});
|
||||
};
|
||||
|
||||
const openRoomInfo = () => {
|
||||
cy.get(".mx_RightPanel_roomSummaryButton").click();
|
||||
return cy.get(".mx_RightPanel");
|
||||
};
|
||||
|
||||
const checkDMRoom = () => {
|
||||
cy.contains(".mx_TextualEvent", "Alice invited Bob").should("exist");
|
||||
cy.contains(".mx_RoomView_body .mx_cryptoEvent", "Encryption enabled").should("exist");
|
||||
};
|
||||
|
||||
const startDMWithBob = function(this: CryptoTestContext) {
|
||||
cy.get('.mx_RoomList [aria-label="Start chat"]').click();
|
||||
cy.get('[data-test-id="invite-dialog-input"]').type(this.bob.getUserId());
|
||||
cy.contains(".mx_InviteDialog_tile_nameStack_name", "Bob").click();
|
||||
cy.contains(".mx_InviteDialog_userTile_pill .mx_InviteDialog_userTile_name", "Bob").should("exist");
|
||||
cy.get(".mx_InviteDialog_goButton").click();
|
||||
};
|
||||
|
||||
const testMessages = function(this: CryptoTestContext) {
|
||||
cy.get(".mx_BasicMessageComposer_input").should("have.focus").type("Hey!{enter}");
|
||||
cy.contains(".mx_EventTile_body", "Hey!")
|
||||
.closest(".mx_EventTile")
|
||||
.should("not.have.descendants", ".mx_EventTile_e2eIcon_warning")
|
||||
.should("have.descendants", ".mx_EventTile_receiptSent");
|
||||
|
||||
// Bob sends a response
|
||||
cy.get<Room>("@bobsRoom").then((room) => {
|
||||
this.bob.sendTextMessage(room.roomId, "Hoo!");
|
||||
});
|
||||
cy.contains(".mx_EventTile_body", "Hoo!")
|
||||
.closest(".mx_EventTile")
|
||||
.should("not.have.descendants", ".mx_EventTile_e2eIcon_warning");
|
||||
};
|
||||
|
||||
const bobJoin = function(this: CryptoTestContext) {
|
||||
cy.botJoinRoomByName(this.bob, "Alice").as("bobsRoom");
|
||||
cy.contains(".mx_TextualEvent", "Bob joined the room").should("exist");
|
||||
};
|
||||
|
||||
const handleVerificationRequest = (request: VerificationRequest): Chainable<EmojiMapping[]> => {
|
||||
return cy.wrap(new Promise<EmojiMapping[]>((resolve) => {
|
||||
const onShowSas = (event: ISasEvent) => {
|
||||
resolve(event.sas.emoji);
|
||||
verifier.off("show_sas", onShowSas);
|
||||
event.confirm();
|
||||
verifier.done();
|
||||
};
|
||||
|
||||
const verifier = request.beginKeyVerification("m.sas.v1");
|
||||
verifier.on("show_sas", onShowSas);
|
||||
verifier.verify();
|
||||
}));
|
||||
};
|
||||
|
||||
const verify = function(this: CryptoTestContext) {
|
||||
const bobsVerificationRequestPromise = waitForVerificationRequest(this.bob);
|
||||
|
||||
openRoomInfo().within(() => {
|
||||
cy.get(".mx_RoomSummaryCard_icon_people").click();
|
||||
cy.contains(".mx_EntityTile_name", "Bob").click();
|
||||
cy.contains(".mx_UserInfo_verifyButton", "Verify").click();
|
||||
cy.contains(".mx_AccessibleButton", "Start Verification").click();
|
||||
cy.wrap(bobsVerificationRequestPromise).then((verificationRequest: VerificationRequest) => {
|
||||
verificationRequest.accept();
|
||||
return verificationRequest;
|
||||
}).as("bobsVerificationRequest");
|
||||
cy.contains(".mx_AccessibleButton", "Verify by emoji").click();
|
||||
cy.get<VerificationRequest>("@bobsVerificationRequest").then((request: VerificationRequest) => {
|
||||
return handleVerificationRequest(request).then((emojis: EmojiMapping[]) => {
|
||||
cy.get('.mx_VerificationShowSas_emojiSas_block').then((emojiBlocks) => {
|
||||
emojis.forEach((emoji: EmojiMapping, index: number) => {
|
||||
expect(emojiBlocks[index].textContent.toLowerCase()).to.eq(emoji[0] + emoji[1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
cy.contains(".mx_AccessibleButton", "They match").click();
|
||||
cy.contains("You've successfully verified Bob!").should("exist");
|
||||
cy.contains(".mx_AccessibleButton", "Got it").click();
|
||||
});
|
||||
};
|
||||
|
||||
describe("Cryptography", function() {
|
||||
beforeEach(function() {
|
||||
cy.startSynapse("default").as("synapse").then((synapse: SynapseInstance) => {
|
||||
cy.initTestUser(synapse, "Alice");
|
||||
cy.getBot(synapse, { displayName: "Bob", autoAcceptInvites: false }).as("bob");
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function(this: CryptoTestContext) {
|
||||
cy.stopSynapse(this.synapse);
|
||||
});
|
||||
|
||||
it("setting up secure key backup should work", () => {
|
||||
cy.openUserSettings("Security & Privacy");
|
||||
cy.contains(".mx_AccessibleButton", "Set up Secure Backup").click();
|
||||
cy.get(".mx_Dialog").within(() => {
|
||||
cy.contains(".mx_Dialog_primary", "Continue").click();
|
||||
cy.get(".mx_CreateSecretStorageDialog_recoveryKey code").invoke("text").as("securityKey");
|
||||
// Clicking download instead of Copy because of https://github.com/cypress-io/cypress/issues/2851
|
||||
cy.contains(".mx_AccessibleButton", "Download").click();
|
||||
cy.contains(".mx_Dialog_primary:not([disabled])", "Continue").click();
|
||||
cy.contains(".mx_Dialog_title", "Setting up keys").should("exist");
|
||||
cy.contains(".mx_Dialog_title", "Setting up keys").should("not.exist");
|
||||
});
|
||||
return;
|
||||
});
|
||||
|
||||
it("creating a DM should work, being e2e-encrypted / user verification", function(this: CryptoTestContext) {
|
||||
cy.bootstrapCrossSigning();
|
||||
startDMWithBob.call(this);
|
||||
checkDMRoom();
|
||||
bobJoin.call(this);
|
||||
testMessages.call(this);
|
||||
verify.call(this);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
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 { SynapseInstance } from "../../plugins/synapsedocker";
|
||||
|
||||
describe("Update", () => {
|
||||
let synapse: SynapseInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.startSynapse("default").then(data => {
|
||||
synapse = data;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopSynapse(synapse);
|
||||
});
|
||||
|
||||
it("should navigate to ?updated=$VERSION if realises it is immediately out of date on load", () => {
|
||||
const NEW_VERSION = "some-new-version";
|
||||
|
||||
cy.intercept("/version*", {
|
||||
statusCode: 200,
|
||||
body: NEW_VERSION,
|
||||
headers: {
|
||||
"Content-Type": "test/plain",
|
||||
},
|
||||
}).as("version");
|
||||
|
||||
cy.initTestUser(synapse, "Ursa");
|
||||
|
||||
cy.wait("@version");
|
||||
cy.url().should("contain", "updated=" + NEW_VERSION).then(href => {
|
||||
const url = new URL(href);
|
||||
expect(url.searchParams.get("updated")).to.equal(NEW_VERSION);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
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 { SynapseInstance } from "../../plugins/synapsedocker";
|
||||
|
||||
const STICKER_PICKER_WIDGET_ID = "fake-sticker-picker";
|
||||
const STICKER_PICKER_WIDGET_NAME = "Fake Stickers";
|
||||
const STICKER_NAME = "Test Sticker";
|
||||
const ROOM_NAME_1 = "Sticker Test";
|
||||
const ROOM_NAME_2 = "Sticker Test Two";
|
||||
const STICKER_MESSAGE = JSON.stringify({
|
||||
action: "m.sticker",
|
||||
api: "fromWidget",
|
||||
data: {
|
||||
name: "teststicker",
|
||||
description: STICKER_NAME,
|
||||
file: "test.png",
|
||||
content: {
|
||||
body: STICKER_NAME,
|
||||
msgtype: "m.sticker",
|
||||
url: "mxc://somewhere",
|
||||
},
|
||||
},
|
||||
requestId: "1",
|
||||
widgetId: STICKER_PICKER_WIDGET_ID,
|
||||
});
|
||||
const WIDGET_HTML = `
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Fake Sticker Picker</title>
|
||||
<script>
|
||||
window.onmessage = ev => {
|
||||
if (ev.data.action === 'capabilities') {
|
||||
window.parent.postMessage(Object.assign({
|
||||
response: {
|
||||
capabilities: ["m.sticker"]
|
||||
},
|
||||
}, ev.data), '*');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<button name="Send" id="sendsticker">Press for sticker</button>
|
||||
<script>
|
||||
document.getElementById('sendsticker').onclick = () => {
|
||||
window.parent.postMessage(${STICKER_MESSAGE}, '*')
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
function openStickerPicker() {
|
||||
cy.get('.mx_MessageComposer_buttonMenu').click();
|
||||
cy.get('#stickersButton').click();
|
||||
}
|
||||
|
||||
function sendStickerFromPicker() {
|
||||
// Note: Until https://github.com/cypress-io/cypress/issues/136 is fixed we will need
|
||||
// to use `chromeWebSecurity: false` in our cypress config. Not even cy.origin() can
|
||||
// break into the iframe for us :(
|
||||
cy.accessIframe(`iframe[title="${STICKER_PICKER_WIDGET_NAME}"]`).within({}, () => {
|
||||
cy.get("#sendsticker").should('exist').click();
|
||||
});
|
||||
|
||||
// Sticker picker should close itself after sending.
|
||||
cy.get(".mx_AppTileFullWidth#stickers").should('not.exist');
|
||||
}
|
||||
|
||||
function expectTimelineSticker(roomId: string) {
|
||||
// Make sure it's in the right room
|
||||
cy.get('.mx_EventTile_sticker > a')
|
||||
.should("have.attr", "href")
|
||||
.and("include", `/${roomId}/`);
|
||||
|
||||
// Make sure the image points at the sticker image
|
||||
cy.get<HTMLImageElement>(`img[alt="${STICKER_NAME}"]`)
|
||||
.should("have.attr", "src")
|
||||
.and("match", /thumbnail\/somewhere\?/);
|
||||
}
|
||||
|
||||
describe("Stickers", () => {
|
||||
// We spin up a web server for the sticker picker so that we're not testing to see if
|
||||
// sysadmins can deploy sticker pickers on the same Element domain - we actually want
|
||||
// to make sure that cross-origin postMessage works properly. This makes it difficult
|
||||
// to write the test though, as we have to juggle iframe logistics.
|
||||
//
|
||||
// See sendStickerFromPicker() for more detail on iframe comms.
|
||||
|
||||
let stickerPickerUrl: string;
|
||||
let synapse: SynapseInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.startSynapse("default").then(data => {
|
||||
synapse = data;
|
||||
|
||||
cy.initTestUser(synapse, "Sally");
|
||||
});
|
||||
cy.serveHtmlFile(WIDGET_HTML).then(url => {
|
||||
stickerPickerUrl = url;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopSynapse(synapse);
|
||||
cy.stopWebServers();
|
||||
});
|
||||
|
||||
it('should send a sticker to multiple rooms', () => {
|
||||
cy.createRoom({
|
||||
name: ROOM_NAME_1,
|
||||
}).as("roomId1");
|
||||
cy.createRoom({
|
||||
name: ROOM_NAME_2,
|
||||
}).as("roomId2");
|
||||
cy.setAccountData("m.widgets", {
|
||||
[STICKER_PICKER_WIDGET_ID]: {
|
||||
content: {
|
||||
type: "m.stickerpicker",
|
||||
name: STICKER_PICKER_WIDGET_NAME,
|
||||
url: stickerPickerUrl,
|
||||
},
|
||||
id: STICKER_PICKER_WIDGET_ID,
|
||||
},
|
||||
}).as("stickers");
|
||||
|
||||
cy.all([
|
||||
cy.get<string>("@roomId1"),
|
||||
cy.get<string>("@roomId2"),
|
||||
cy.get<{}>("@stickers"), // just want to wait for it to be set up
|
||||
]).then(([roomId1, roomId2]) => {
|
||||
cy.viewRoomByName(ROOM_NAME_1);
|
||||
cy.url().should("contain", `/#/room/${roomId1}`);
|
||||
openStickerPicker();
|
||||
sendStickerFromPicker();
|
||||
expectTimelineSticker(roomId1);
|
||||
|
||||
// Ensure that when we switch to a different room that the sticker
|
||||
// goes to the right place
|
||||
cy.viewRoomByName(ROOM_NAME_2);
|
||||
cy.url().should("contain", `/#/room/${roomId2}`);
|
||||
openStickerPicker();
|
||||
sendStickerFromPicker();
|
||||
expectTimelineSticker(roomId2);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user