1
0
mirror of https://github.com/matrix-org/matrix-react-sdk.git synced 2025-12-23 21:02:09 +03:00

Apply prettier formatting

This commit is contained in:
Michael Weimann
2022-12-12 12:24:14 +01:00
parent 1cac306093
commit 526645c791
1576 changed files with 65385 additions and 62478 deletions

View File

@@ -23,7 +23,7 @@ describe("Composer", () => {
let synapse: SynapseInstance;
beforeEach(() => {
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
});
});
@@ -42,26 +42,26 @@ describe("Composer", () => {
it("sends a message when you click send or press Enter", () => {
// Type a message
cy.get('div[contenteditable=true]').type('my message 0');
cy.get("div[contenteditable=true]").type("my message 0");
// It has not been sent yet
cy.contains('.mx_EventTile_body', 'my message 0').should('not.exist');
cy.contains(".mx_EventTile_body", "my message 0").should("not.exist");
// Click send
cy.get('div[aria-label="Send message"]').click();
// It has been sent
cy.contains('.mx_EventTile_body', 'my message 0');
cy.contains(".mx_EventTile_body", "my message 0");
// Type another and press Enter afterwards
cy.get('div[contenteditable=true]').type('my message 1{enter}');
cy.get("div[contenteditable=true]").type("my message 1{enter}");
// It was sent
cy.contains('.mx_EventTile_body', 'my message 1');
cy.contains(".mx_EventTile_body", "my message 1");
});
it("can write formatted text", () => {
cy.get('div[contenteditable=true]').type('my bold{ctrl+b} message');
cy.get("div[contenteditable=true]").type("my bold{ctrl+b} message");
cy.get('div[aria-label="Send message"]').click();
// Note: both "bold" and "message" are bold, which is probably surprising
cy.contains('.mx_EventTile_body strong', 'bold message');
cy.contains(".mx_EventTile_body strong", "bold message");
});
it("should allow user to input emoji via graphical picker", () => {
@@ -74,7 +74,7 @@ describe("Composer", () => {
});
cy.get(".mx_ContextualMenu_background").click(); // Close emoji picker
cy.get('div[contenteditable=true]').type("{enter}"); // Send message
cy.get("div[contenteditable=true]").type("{enter}"); // Send message
cy.contains(".mx_EventTile_body", "😇");
});
@@ -86,14 +86,14 @@ describe("Composer", () => {
it("only sends when you press Ctrl+Enter", () => {
// Type a message and press Enter
cy.get('div[contenteditable=true]').type('my message 3{enter}');
cy.get("div[contenteditable=true]").type("my message 3{enter}");
// It has not been sent yet
cy.contains('.mx_EventTile_body', 'my message 3').should('not.exist');
cy.contains(".mx_EventTile_body", "my message 3").should("not.exist");
// Press Ctrl+Enter
cy.get('div[contenteditable=true]').type('{ctrl+enter}');
cy.get("div[contenteditable=true]").type("{ctrl+enter}");
// It was sent
cy.contains('.mx_EventTile_body', 'my message 3');
cy.contains(".mx_EventTile_body", "my message 3");
});
});
});
@@ -109,28 +109,28 @@ describe("Composer", () => {
it("sends a message when you click send or press Enter", () => {
// Type a message
cy.get('div[contenteditable=true]').type('my message 0');
cy.get("div[contenteditable=true]").type("my message 0");
// It has not been sent yet
cy.contains('.mx_EventTile_body', 'my message 0').should('not.exist');
cy.contains(".mx_EventTile_body", "my message 0").should("not.exist");
// Click send
cy.get('div[aria-label="Send message"]').click();
// It has been sent
cy.contains('.mx_EventTile_body', 'my message 0');
cy.contains(".mx_EventTile_body", "my message 0");
// Type another
cy.get('div[contenteditable=true]').type('my message 1');
cy.get("div[contenteditable=true]").type("my message 1");
// Press enter. Would be nice to just use {enter} but we can't because Cypress
// does not trigger an insertParagraph when you do that.
cy.get('div[contenteditable=true]').trigger('input', { inputType: "insertParagraph" });
cy.get("div[contenteditable=true]").trigger("input", { inputType: "insertParagraph" });
// It was sent
cy.contains('.mx_EventTile_body', 'my message 1');
cy.contains(".mx_EventTile_body", "my message 1");
});
it("can write formatted text", () => {
cy.get('div[contenteditable=true]').type('my {ctrl+b}bold{ctrl+b} message');
cy.get("div[contenteditable=true]").type("my {ctrl+b}bold{ctrl+b} message");
cy.get('div[aria-label="Send message"]').click();
cy.contains('.mx_EventTile_body strong', 'bold');
cy.contains(".mx_EventTile_body strong", "bold");
});
describe("when Ctrl+Enter is required to send", () => {
@@ -140,15 +140,15 @@ describe("Composer", () => {
it("only sends when you press Ctrl+Enter", () => {
// Type a message and press Enter
cy.get('div[contenteditable=true]').type('my message 3');
cy.get('div[contenteditable=true]').trigger('input', { inputType: "insertParagraph" });
cy.get("div[contenteditable=true]").type("my message 3");
cy.get("div[contenteditable=true]").trigger("input", { inputType: "insertParagraph" });
// It has not been sent yet
cy.contains('.mx_EventTile_body', 'my message 3').should('not.exist');
cy.contains(".mx_EventTile_body", "my message 3").should("not.exist");
// Press Ctrl+Enter
cy.get('div[contenteditable=true]').type('{ctrl+enter}');
cy.get("div[contenteditable=true]").type("{ctrl+enter}");
// It was sent
cy.contains('.mx_EventTile_body', 'my message 3');
cy.contains(".mx_EventTile_body", "my message 3");
});
});
});

View File

@@ -29,7 +29,7 @@ describe("Create Room", () => {
let synapse: SynapseInstance;
beforeEach(() => {
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Jim");

View File

@@ -28,7 +28,7 @@ interface CryptoTestContext extends Mocha.Context {
}
const waitForVerificationRequest = (cli: MatrixClient): Promise<VerificationRequest> => {
return new Promise<VerificationRequest>(resolve => {
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);
@@ -49,7 +49,7 @@ const checkDMRoom = () => {
cy.contains(".mx_RoomView_body .mx_cryptoEvent", "Encryption enabled").should("exist");
};
const startDMWithBob = function(this: CryptoTestContext) {
const startDMWithBob = function (this: CryptoTestContext) {
cy.get('.mx_RoomList [aria-label="Start chat"]').click();
cy.get('[data-testid="invite-dialog-input"]').type(this.bob.getUserId());
cy.contains(".mx_InviteDialog_tile_nameStack_name", "Bob").click();
@@ -57,11 +57,13 @@ const startDMWithBob = function(this: CryptoTestContext) {
cy.get(".mx_InviteDialog_goButton").click();
};
const testMessages = function(this: CryptoTestContext) {
const testMessages = function (this: CryptoTestContext) {
// check the invite message
cy.contains(".mx_EventTile_body", "Hey!").closest(".mx_EventTile").within(() => {
cy.get(".mx_EventTile_e2eIcon_warning").should("not.exist");
});
cy.contains(".mx_EventTile_body", "Hey!")
.closest(".mx_EventTile")
.within(() => {
cy.get(".mx_EventTile_e2eIcon_warning").should("not.exist");
});
// Bob sends a response
cy.get<Room>("@bobsRoom").then((room) => {
@@ -72,28 +74,30 @@ const testMessages = function(this: CryptoTestContext) {
.should("not.have.descendants", ".mx_EventTile_e2eIcon_warning");
};
const bobJoin = function(this: CryptoTestContext) {
cy.window({ log: false }).then(async win => {
const bobRooms = this.bob.getRooms();
if (!bobRooms.length) {
await new Promise<void>(resolve => {
const onMembership = (_event) => {
this.bob.off(win.matrixcs.RoomMemberEvent.Membership, onMembership);
resolve();
};
this.bob.on(win.matrixcs.RoomMemberEvent.Membership, onMembership);
});
}
}).then(() => {
cy.botJoinRoomByName(this.bob, "Alice").as("bobsRoom");
});
const bobJoin = function (this: CryptoTestContext) {
cy.window({ log: false })
.then(async (win) => {
const bobRooms = this.bob.getRooms();
if (!bobRooms.length) {
await new Promise<void>((resolve) => {
const onMembership = (_event) => {
this.bob.off(win.matrixcs.RoomMemberEvent.Membership, onMembership);
resolve();
};
this.bob.on(win.matrixcs.RoomMemberEvent.Membership, onMembership);
});
}
})
.then(() => {
cy.botJoinRoomByName(this.bob, "Alice").as("bobsRoom");
});
cy.contains(".mx_TextualEvent", "Bob joined the room").should("exist");
};
/** configure the given MatrixClient to auto-accept any invites */
function autoJoin(client: MatrixClient) {
cy.window({ log: false }).then(async win => {
cy.window({ log: false }).then(async (win) => {
client.on(win.matrixcs.RoomMemberEvent.Membership, (event, member) => {
if (member.membership === "invite" && member.userId === client.getUserId()) {
client.joinRoom(member.roomId);
@@ -103,21 +107,23 @@ function autoJoin(client: MatrixClient) {
}
const handleVerificationRequest = (request: VerificationRequest): Chainable<EmojiMapping[]> => {
return cy.wrap(new Promise<EmojiMapping[]>((resolve) => {
const onShowSas = (event: ISasEvent) => {
verifier.off("show_sas", onShowSas);
event.confirm();
verifier.done();
resolve(event.sas.emoji);
};
return cy.wrap(
new Promise<EmojiMapping[]>((resolve) => {
const onShowSas = (event: ISasEvent) => {
verifier.off("show_sas", onShowSas);
event.confirm();
verifier.done();
resolve(event.sas.emoji);
};
const verifier = request.beginKeyVerification("m.sas.v1");
verifier.on("show_sas", onShowSas);
verifier.verify();
}));
const verifier = request.beginKeyVerification("m.sas.v1");
verifier.on("show_sas", onShowSas);
verifier.verify();
}),
);
};
const verify = function(this: CryptoTestContext) {
const verify = function (this: CryptoTestContext) {
const bobsVerificationRequestPromise = waitForVerificationRequest(this.bob);
openRoomInfo().within(() => {
@@ -125,14 +131,16 @@ const verify = function(this: CryptoTestContext) {
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.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) => {
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]);
});
@@ -145,15 +153,17 @@ const verify = function(this: CryptoTestContext) {
});
};
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");
});
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) {
afterEach(function (this: CryptoTestContext) {
cy.stopSynapse(this.synapse);
});
@@ -172,27 +182,24 @@ describe("Cryptography", function() {
return;
});
it("creating a DM should work, being e2e-encrypted / user verification", function(this: CryptoTestContext) {
it("creating a DM should work, being e2e-encrypted / user verification", function (this: CryptoTestContext) {
cy.bootstrapCrossSigning();
startDMWithBob.call(this);
// send first message
cy.get(".mx_BasicMessageComposer_input")
.click()
.should("have.focus")
.type("Hey!{enter}");
cy.get(".mx_BasicMessageComposer_input").click().should("have.focus").type("Hey!{enter}");
checkDMRoom();
bobJoin.call(this);
testMessages.call(this);
verify.call(this);
});
it("should allow verification when there is no existing DM", function(this: CryptoTestContext) {
it("should allow verification when there is no existing DM", function (this: CryptoTestContext) {
cy.bootstrapCrossSigning();
autoJoin(this.bob);
/* we need to have a room with the other user present, so we can open the verification panel */
let roomId: string;
cy.createRoom({ name: "TestRoom", invite: [this.bob.getUserId()] }).then(_room1Id => {
cy.createRoom({ name: "TestRoom", invite: [this.bob.getUserId()] }).then((_room1Id) => {
roomId = _room1Id;
cy.log(`Created test room ${roomId}`);
cy.visit(`/#/room/${roomId}`);

View File

@@ -24,19 +24,14 @@ import { SynapseInstance } from "../../plugins/synapsedocker";
import Chainable = Cypress.Chainable;
const sendEvent = (roomId: string): Chainable<ISendEventResponse> => {
return cy.sendEvent(
roomId,
null,
"m.room.message" as EventType,
MessageEvent.from("Message").serialize().content,
);
return cy.sendEvent(roomId, null, "m.room.message" as EventType, MessageEvent.from("Message").serialize().content);
};
describe("Editing", () => {
let synapse: SynapseInstance;
beforeEach(() => {
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Edith").then(() => {
cy.injectAxe();
@@ -50,7 +45,7 @@ describe("Editing", () => {
});
it("should close the composer when clicking save after making a change and undoing it", () => {
cy.get<string>("@roomId").then(roomId => {
cy.get<string>("@roomId").then((roomId) => {
sendEvent(roomId);
cy.visit("/#/room/" + roomId);
});

View File

@@ -77,18 +77,18 @@ describe("Integration Manager: Get OpenID Token", () => {
let integrationManagerUrl: string;
beforeEach(() => {
cy.serveHtmlFile(INTEGRATION_MANAGER_HTML).then(url => {
cy.serveHtmlFile(INTEGRATION_MANAGER_HTML).then((url) => {
integrationManagerUrl = url;
});
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, USER_DISPLAY_NAME, () => {
cy.window().then(win => {
cy.window().then((win) => {
win.localStorage.setItem("mx_scalar_token", INTEGRATION_MANAGER_TOKEN);
win.localStorage.setItem(`mx_scalar_token_at_${integrationManagerUrl}`, INTEGRATION_MANAGER_TOKEN);
});
}).then(user => {
}).then((user) => {
testUser = user;
});
@@ -107,8 +107,8 @@ describe("Integration Manager: Get OpenID Token", () => {
}).as("integrationManager");
// Succeed when checking the token is valid
cy.intercept(`${integrationManagerUrl}/account?scalar_token=${INTEGRATION_MANAGER_TOKEN}*`, req => {
req.continue(res => {
cy.intercept(`${integrationManagerUrl}/account?scalar_token=${INTEGRATION_MANAGER_TOKEN}*`, (req) => {
req.continue((res) => {
return res.send(200, {
user_id: testUser.userId,
});
@@ -127,16 +127,14 @@ describe("Integration Manager: Get OpenID Token", () => {
});
it("should successfully obtain an openID token", () => {
cy.all([
cy.get<{}>("@integrationManager"),
]).then(() => {
cy.all([cy.get<{}>("@integrationManager")]).then(() => {
cy.viewRoomByName(ROOM_NAME);
openIntegrationManager();
sendActionFromIntegrationManager(integrationManagerUrl);
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
cy.get("#message-response").should('include.text', 'access_token');
cy.get("#message-response").should("include.text", "access_token");
});
});
});

View File

@@ -87,8 +87,9 @@ function expectKickedMessage(shouldExist: boolean) {
cy.get(".mx_GenericEventListSummary_toggle[aria-expanded=false]").click({ multiple: true });
// Check for the event message (or lack thereof)
cy.contains(".mx_EventTile_line", `${USER_DISPLAY_NAME} removed ${BOT_DISPLAY_NAME}: ${KICK_REASON}`)
.should(shouldExist ? "exist" : "not.exist");
cy.contains(".mx_EventTile_line", `${USER_DISPLAY_NAME} removed ${BOT_DISPLAY_NAME}: ${KICK_REASON}`).should(
shouldExist ? "exist" : "not.exist",
);
}
describe("Integration Manager: Kick", () => {
@@ -97,18 +98,18 @@ describe("Integration Manager: Kick", () => {
let integrationManagerUrl: string;
beforeEach(() => {
cy.serveHtmlFile(INTEGRATION_MANAGER_HTML).then(url => {
cy.serveHtmlFile(INTEGRATION_MANAGER_HTML).then((url) => {
integrationManagerUrl = url;
});
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, USER_DISPLAY_NAME, () => {
cy.window().then(win => {
cy.window().then((win) => {
win.localStorage.setItem("mx_scalar_token", INTEGRATION_MANAGER_TOKEN);
win.localStorage.setItem(`mx_scalar_token_at_${integrationManagerUrl}`, INTEGRATION_MANAGER_TOKEN);
});
}).then(user => {
}).then((user) => {
testUser = user;
});
@@ -127,8 +128,8 @@ describe("Integration Manager: Kick", () => {
}).as("integrationManager");
// Succeed when checking the token is valid
cy.intercept(`${integrationManagerUrl}/account?scalar_token=${INTEGRATION_MANAGER_TOKEN}*`, req => {
req.continue(res => {
cy.intercept(`${integrationManagerUrl}/account?scalar_token=${INTEGRATION_MANAGER_TOKEN}*`, (req) => {
req.continue((res) => {
return res.send(200, {
user_id: testUser.userId,
});
@@ -149,103 +150,100 @@ describe("Integration Manager: Kick", () => {
});
it("should kick the target", () => {
cy.all([
cy.get<MatrixClient>("@bob"),
cy.get<string>("@roomId"),
cy.get<{}>("@integrationManager"),
]).then(([targetUser, roomId]) => {
const targetUserId = targetUser.getUserId();
cy.viewRoomByName(ROOM_NAME);
cy.inviteUser(roomId, targetUserId);
cy.contains(`${BOT_DISPLAY_NAME} joined the room`).should('exist');
cy.all([cy.get<MatrixClient>("@bob"), cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(
([targetUser, roomId]) => {
const targetUserId = targetUser.getUserId();
cy.viewRoomByName(ROOM_NAME);
cy.inviteUser(roomId, targetUserId);
cy.contains(`${BOT_DISPLAY_NAME} joined the room`).should("exist");
openIntegrationManager();
sendActionFromIntegrationManager(integrationManagerUrl, roomId, targetUserId);
closeIntegrationManager(integrationManagerUrl);
expectKickedMessage(true);
});
openIntegrationManager();
sendActionFromIntegrationManager(integrationManagerUrl, roomId, targetUserId);
closeIntegrationManager(integrationManagerUrl);
expectKickedMessage(true);
},
);
});
it("should not kick the target if lacking permissions", () => {
cy.all([
cy.get<MatrixClient>("@bob"),
cy.get<string>("@roomId"),
cy.get<{}>("@integrationManager"),
]).then(([targetUser, roomId]) => {
const targetUserId = targetUser.getUserId();
cy.viewRoomByName(ROOM_NAME);
cy.inviteUser(roomId, targetUserId);
cy.contains(`${BOT_DISPLAY_NAME} joined the room`).should('exist');
cy.getClient().then(async client => {
await client.sendStateEvent(roomId, 'm.room.power_levels', {
kick: 50,
users: {
[testUser.userId]: 0,
},
});
}).then(() => {
openIntegrationManager();
sendActionFromIntegrationManager(integrationManagerUrl, roomId, targetUserId);
closeIntegrationManager(integrationManagerUrl);
expectKickedMessage(false);
});
});
cy.all([cy.get<MatrixClient>("@bob"), cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(
([targetUser, roomId]) => {
const targetUserId = targetUser.getUserId();
cy.viewRoomByName(ROOM_NAME);
cy.inviteUser(roomId, targetUserId);
cy.contains(`${BOT_DISPLAY_NAME} joined the room`).should("exist");
cy.getClient()
.then(async (client) => {
await client.sendStateEvent(roomId, "m.room.power_levels", {
kick: 50,
users: {
[testUser.userId]: 0,
},
});
})
.then(() => {
openIntegrationManager();
sendActionFromIntegrationManager(integrationManagerUrl, roomId, targetUserId);
closeIntegrationManager(integrationManagerUrl);
expectKickedMessage(false);
});
},
);
});
it("should no-op if the target already left", () => {
cy.all([
cy.get<MatrixClient>("@bob"),
cy.get<string>("@roomId"),
cy.get<{}>("@integrationManager"),
]).then(([targetUser, roomId]) => {
const targetUserId = targetUser.getUserId();
cy.viewRoomByName(ROOM_NAME);
cy.inviteUser(roomId, targetUserId);
cy.contains(`${BOT_DISPLAY_NAME} joined the room`).should('exist').then(async () => {
await targetUser.leave(roomId);
}).then(() => {
openIntegrationManager();
sendActionFromIntegrationManager(integrationManagerUrl, roomId, targetUserId);
closeIntegrationManager(integrationManagerUrl);
expectKickedMessage(false);
});
});
cy.all([cy.get<MatrixClient>("@bob"), cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(
([targetUser, roomId]) => {
const targetUserId = targetUser.getUserId();
cy.viewRoomByName(ROOM_NAME);
cy.inviteUser(roomId, targetUserId);
cy.contains(`${BOT_DISPLAY_NAME} joined the room`)
.should("exist")
.then(async () => {
await targetUser.leave(roomId);
})
.then(() => {
openIntegrationManager();
sendActionFromIntegrationManager(integrationManagerUrl, roomId, targetUserId);
closeIntegrationManager(integrationManagerUrl);
expectKickedMessage(false);
});
},
);
});
it("should no-op if the target was banned", () => {
cy.all([
cy.get<MatrixClient>("@bob"),
cy.get<string>("@roomId"),
cy.get<{}>("@integrationManager"),
]).then(([targetUser, roomId]) => {
const targetUserId = targetUser.getUserId();
cy.viewRoomByName(ROOM_NAME);
cy.inviteUser(roomId, targetUserId);
cy.contains(`${BOT_DISPLAY_NAME} joined the room`).should('exist');
cy.getClient().then(async client => {
await client.ban(roomId, targetUserId);
}).then(() => {
cy.all([cy.get<MatrixClient>("@bob"), cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(
([targetUser, roomId]) => {
const targetUserId = targetUser.getUserId();
cy.viewRoomByName(ROOM_NAME);
cy.inviteUser(roomId, targetUserId);
cy.contains(`${BOT_DISPLAY_NAME} joined the room`).should("exist");
cy.getClient()
.then(async (client) => {
await client.ban(roomId, targetUserId);
})
.then(() => {
openIntegrationManager();
sendActionFromIntegrationManager(integrationManagerUrl, roomId, targetUserId);
closeIntegrationManager(integrationManagerUrl);
expectKickedMessage(false);
});
},
);
});
it("should no-op if the target was never a room member", () => {
cy.all([cy.get<MatrixClient>("@bob"), cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(
([targetUser, roomId]) => {
const targetUserId = targetUser.getUserId();
cy.viewRoomByName(ROOM_NAME);
openIntegrationManager();
sendActionFromIntegrationManager(integrationManagerUrl, roomId, targetUserId);
closeIntegrationManager(integrationManagerUrl);
expectKickedMessage(false);
});
});
});
it("should no-op if the target was never a room member", () => {
cy.all([
cy.get<MatrixClient>("@bob"),
cy.get<string>("@roomId"),
cy.get<{}>("@integrationManager"),
]).then(([targetUser, roomId]) => {
const targetUserId = targetUser.getUserId();
cy.viewRoomByName(ROOM_NAME);
openIntegrationManager();
sendActionFromIntegrationManager(integrationManagerUrl, roomId, targetUserId);
closeIntegrationManager(integrationManagerUrl);
expectKickedMessage(false);
});
},
);
});
});

View File

@@ -31,11 +31,11 @@ describe("Lazy Loading", () => {
const charlies: Charly[] = [];
beforeEach(() => {
cy.window().then(win => {
cy.window().then((win) => {
win.localStorage.setItem("mx_lhs_size", "0"); // Collapse left panel for these tests
});
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Alice");
@@ -44,7 +44,7 @@ describe("Lazy Loading", () => {
displayName: "Bob",
startClient: false,
autoAcceptInvites: false,
}).then(_bob => {
}).then((_bob) => {
bob = _bob;
});
@@ -54,7 +54,7 @@ describe("Lazy Loading", () => {
displayName,
startClient: false,
autoAcceptInvites: false,
}).then(client => {
}).then((client) => {
charlies[i - 1] = { displayName, client };
});
}
@@ -71,15 +71,22 @@ describe("Lazy Loading", () => {
const charlyMsg2 = "how's it going??";
function setupRoomWithBobAliceAndCharlies(charlies: Charly[]) {
cy.window({ log: false }).then(win => {
return cy.wrap(bob.createRoom({
name,
room_alias_name: "lltest",
visibility: win.matrixcs.Visibility.Public,
}).then(r => r.room_id), { log: false }).as("roomId");
cy.window({ log: false }).then((win) => {
return cy
.wrap(
bob
.createRoom({
name,
room_alias_name: "lltest",
visibility: win.matrixcs.Visibility.Public,
})
.then((r) => r.room_id),
{ log: false },
)
.as("roomId");
});
cy.get<string>("@roomId").then(async roomId => {
cy.get<string>("@roomId").then(async (roomId) => {
for (const charly of charlies) {
await charly.client.joinRoom(alias);
}
@@ -122,13 +129,13 @@ describe("Lazy Loading", () => {
function checkMemberList(charlies: Charly[]) {
getMemberInMemberlist("Alice").should("exist");
getMemberInMemberlist("Bob").should("exist");
charlies.forEach(charly => {
charlies.forEach((charly) => {
getMemberInMemberlist(charly.displayName).should("exist");
});
}
function checkMemberListLacksCharlies(charlies: Charly[]) {
charlies.forEach(charly => {
charlies.forEach((charly) => {
getMemberInMemberlist(charly.displayName).should("not.exist");
});
}
@@ -136,7 +143,7 @@ describe("Lazy Loading", () => {
function joinCharliesWhileAliceIsOffline(charlies: Charly[]) {
cy.goOffline();
cy.get<string>("@roomId").then(async roomId => {
cy.get<string>("@roomId").then(async (roomId) => {
for (const charly of charlies) {
await charly.client.joinRoom(alias);
}
@@ -163,7 +170,7 @@ describe("Lazy Loading", () => {
joinCharliesWhileAliceIsOffline(charly6to10);
checkMemberList(charly6to10);
cy.get<string>("@roomId").then(async roomId => {
cy.get<string>("@roomId").then(async (roomId) => {
for (const charly of charlies) {
await charly.client.leave(roomId);
}

View File

@@ -31,10 +31,10 @@ describe("Location sharing", () => {
};
beforeEach(() => {
cy.window().then(win => {
cy.window().then((win) => {
win.localStorage.setItem("mx_lhs_size", "0"); // Collapse left panel for these tests
});
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Tom");
@@ -47,31 +47,28 @@ describe("Location sharing", () => {
it("sends and displays pin drop location message successfully", () => {
let roomId: string;
cy.createRoom({}).then(_roomId => {
cy.createRoom({}).then((_roomId) => {
roomId = _roomId;
cy.visit('/#/room/' + roomId);
cy.visit("/#/room/" + roomId);
});
cy.openMessageComposerOptions().within(() => {
cy.get('[aria-label="Location"]').click();
});
selectLocationShareTypeOption('Pin').click();
selectLocationShareTypeOption("Pin").click();
cy.get('#mx_LocationPicker_map').click('center');
cy.get("#mx_LocationPicker_map").click("center");
submitShareLocation();
cy.get(".mx_RoomView_body .mx_EventTile .mx_MLocationBody", { timeout: 10000 })
.should('exist')
.click();
cy.get(".mx_RoomView_body .mx_EventTile .mx_MLocationBody", { timeout: 10000 }).should("exist").click();
// clicking location tile opens maximised map
cy.get('.mx_LocationViewDialog_wrapper').should('exist');
cy.get(".mx_LocationViewDialog_wrapper").should("exist");
cy.get('[aria-label="Close dialog"]').click();
cy.get('.mx_Marker')
.should('exist');
cy.get(".mx_Marker").should("exist");
});
});

View File

@@ -24,7 +24,7 @@ describe("Consent", () => {
let synapse: SynapseInstance;
beforeEach(() => {
cy.startSynapse("consent").then(data => {
cy.startSynapse("consent").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Bob");
@@ -37,7 +37,7 @@ describe("Consent", () => {
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 => {
cy.window().then((win) => {
win.mxMatrixClientPeg.matrixClient.createRoom({}).catch(() => {});
// Stub `window.open` - clicking the primary button below will call it
@@ -50,7 +50,7 @@ describe("Consent", () => {
cy.get(".mx_Dialog_primary").click();
});
cy.get<SinonStub>("@windowOpen").then(stub => {
cy.get<SinonStub>("@windowOpen").then((stub) => {
const url = stub.getCall(0).args[0];
// Go to Synapse's consent page and accept it

View File

@@ -34,7 +34,7 @@ describe("Login", () => {
const password = "p4s5W0rD";
beforeEach(() => {
cy.startSynapse("consent").then(data => {
cy.startSynapse("consent").then((data) => {
synapse = data;
cy.registerUser(synapse, username, password);
cy.visit("/#/login");
@@ -52,19 +52,19 @@ describe("Login", () => {
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_ServerPickerDialog").should("not.exist");
cy.get("#mx_LoginForm_username").type(username);
cy.get("#mx_LoginForm_password").type(password);
cy.get(".mx_Login_submit").click();
cy.url().should('contain', '/#/home', { timeout: 30000 });
cy.url().should("contain", "/#/home", { timeout: 30000 });
});
});
describe("logout", () => {
beforeEach(() => {
cy.startSynapse("consent").then(data => {
cy.startSynapse("consent").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Erin");
});

View File

@@ -33,17 +33,17 @@ describe("Polls", () => {
};
const createPoll = ({ title, options }: CreatePollOptions) => {
if (options.length < 2) {
throw new Error('Poll must have at least two options');
throw new Error("Poll must have at least two options");
}
cy.get('.mx_PollCreateDialog').within((pollCreateDialog) => {
cy.get('#poll-topic-input').type(title);
cy.get(".mx_PollCreateDialog").within((pollCreateDialog) => {
cy.get("#poll-topic-input").type(title);
options.forEach((option, index) => {
const optionId = `#pollcreate_option_${index}`;
// click 'add option' button if needed
if (pollCreateDialog.find(optionId).length === 0) {
cy.get('.mx_PollCreateDialog_addOption').scrollIntoView().click();
cy.get(".mx_PollCreateDialog_addOption").scrollIntoView().click();
}
cy.get(optionId).scrollIntoView().type(option);
});
@@ -56,34 +56,32 @@ describe("Polls", () => {
};
const getPollOption = (pollId: string, optionText: string): Chainable<JQuery> => {
return getPollTile(pollId).contains('.mx_MPollBody_option .mx_StyledRadioButton', optionText);
return getPollTile(pollId).contains(".mx_MPollBody_option .mx_StyledRadioButton", optionText);
};
const expectPollOptionVoteCount = (pollId: string, optionText: string, votes: number): void => {
getPollOption(pollId, optionText).within(() => {
cy.get('.mx_MPollBody_optionVoteCount').should('contain', `${votes} vote`);
cy.get(".mx_MPollBody_optionVoteCount").should("contain", `${votes} vote`);
});
};
const botVoteForOption = (bot: MatrixClient, roomId: string, pollId: string, optionText: string): void => {
getPollOption(pollId, optionText).within(ref => {
cy.get('input[type="radio"]').invoke('attr', 'value').then(optionId => {
const pollVote = PollResponseEvent.from([optionId], pollId).serialize();
bot.sendEvent(
roomId,
pollVote.type,
pollVote.content,
);
});
getPollOption(pollId, optionText).within((ref) => {
cy.get('input[type="radio"]')
.invoke("attr", "value")
.then((optionId) => {
const pollVote = PollResponseEvent.from([optionId], pollId).serialize();
bot.sendEvent(roomId, pollVote.type, pollVote.content);
});
});
};
beforeEach(() => {
cy.enableLabsFeature("feature_thread");
cy.window().then(win => {
cy.window().then((win) => {
win.localStorage.setItem("mx_lhs_size", "0"); // Collapse left panel for these tests
});
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Tom");
@@ -96,15 +94,15 @@ describe("Polls", () => {
it("should be creatable and votable", () => {
let bot: MatrixClient;
cy.getBot(synapse, { displayName: "BotBob" }).then(_bot => {
cy.getBot(synapse, { displayName: "BotBob" }).then((_bot) => {
bot = _bot;
});
let roomId: string;
cy.createRoom({}).then(_roomId => {
cy.createRoom({}).then((_roomId) => {
roomId = _roomId;
cy.inviteUser(roomId, bot.getUserId());
cy.visit('/#/room/' + roomId);
cy.visit("/#/room/" + roomId);
// wait until Bob joined
cy.contains(".mx_TextualEvent", "BotBob joined the room").should("exist");
});
@@ -113,34 +111,35 @@ describe("Polls", () => {
cy.get('[aria-label="Poll"]').click();
});
cy.get('.mx_CompoundDialog').percySnapshotElement('Polls Composer');
cy.get(".mx_CompoundDialog").percySnapshotElement("Polls Composer");
const pollParams = {
title: 'Does the polls feature work?',
options: ['Yes', 'No', 'Maybe'],
title: "Does the polls feature work?",
options: ["Yes", "No", "Maybe"],
};
createPoll(pollParams);
// Wait for message to send, get its ID and save as @pollId
cy.contains(".mx_RoomView_body .mx_EventTile[data-scroll-tokens]", pollParams.title)
.invoke("attr", "data-scroll-tokens").as("pollId");
.invoke("attr", "data-scroll-tokens")
.as("pollId");
cy.get<string>("@pollId").then(pollId => {
getPollTile(pollId).percySnapshotElement('Polls Timeline tile - no votes', { percyCSS: hideTimestampCSS });
cy.get<string>("@pollId").then((pollId) => {
getPollTile(pollId).percySnapshotElement("Polls Timeline tile - no votes", { percyCSS: hideTimestampCSS });
// Bot votes 'Maybe' in the poll
botVoteForOption(bot, roomId, pollId, pollParams.options[2]);
// no votes shown until I vote, check bots vote has arrived
cy.get('.mx_MPollBody_totalVotes').should('contain', '1 vote cast');
cy.get(".mx_MPollBody_totalVotes").should("contain", "1 vote cast");
// vote 'Maybe'
getPollOption(pollId, pollParams.options[2]).click('topLeft');
getPollOption(pollId, pollParams.options[2]).click("topLeft");
// both me and bot have voted Maybe
expectPollOptionVoteCount(pollId, pollParams.options[2], 2);
// change my vote to 'Yes'
getPollOption(pollId, pollParams.options[0]).click('topLeft');
getPollOption(pollId, pollParams.options[0]).click("topLeft");
// 1 vote for yes
expectPollOptionVoteCount(pollId, pollParams.options[0], 1);
@@ -161,15 +160,15 @@ describe("Polls", () => {
it("should be editable from context menu if no votes have been cast", () => {
let bot: MatrixClient;
cy.getBot(synapse, { displayName: "BotBob" }).then(_bot => {
cy.getBot(synapse, { displayName: "BotBob" }).then((_bot) => {
bot = _bot;
});
let roomId: string;
cy.createRoom({}).then(_roomId => {
cy.createRoom({}).then((_roomId) => {
roomId = _roomId;
cy.inviteUser(roomId, bot.getUserId());
cy.visit('/#/room/' + roomId);
cy.visit("/#/room/" + roomId);
});
cy.openMessageComposerOptions().within(() => {
@@ -177,40 +176,42 @@ describe("Polls", () => {
});
const pollParams = {
title: 'Does the polls feature work?',
options: ['Yes', 'No', 'Maybe'],
title: "Does the polls feature work?",
options: ["Yes", "No", "Maybe"],
};
createPoll(pollParams);
// Wait for message to send, get its ID and save as @pollId
cy.get(".mx_RoomView_body .mx_EventTile").contains(".mx_EventTile[data-scroll-tokens]", pollParams.title)
.invoke("attr", "data-scroll-tokens").as("pollId");
cy.get(".mx_RoomView_body .mx_EventTile")
.contains(".mx_EventTile[data-scroll-tokens]", pollParams.title)
.invoke("attr", "data-scroll-tokens")
.as("pollId");
cy.get<string>("@pollId").then(pollId => {
cy.get<string>("@pollId").then((pollId) => {
// Open context menu
getPollTile(pollId).rightclick();
// Select edit item
cy.get('.mx_ContextualMenu').within(() => {
cy.get(".mx_ContextualMenu").within(() => {
cy.get('[aria-label="Edit"]').click();
});
// Expect poll editing dialog
cy.get('.mx_PollCreateDialog');
cy.get(".mx_PollCreateDialog");
});
});
it("should not be editable from context menu if votes have been cast", () => {
let bot: MatrixClient;
cy.getBot(synapse, { displayName: "BotBob" }).then(_bot => {
cy.getBot(synapse, { displayName: "BotBob" }).then((_bot) => {
bot = _bot;
});
let roomId: string;
cy.createRoom({}).then(_roomId => {
cy.createRoom({}).then((_roomId) => {
roomId = _roomId;
cy.inviteUser(roomId, bot.getUserId());
cy.visit('/#/room/' + roomId);
cy.visit("/#/room/" + roomId);
});
cy.openMessageComposerOptions().within(() => {
@@ -218,51 +219,53 @@ describe("Polls", () => {
});
const pollParams = {
title: 'Does the polls feature work?',
options: ['Yes', 'No', 'Maybe'],
title: "Does the polls feature work?",
options: ["Yes", "No", "Maybe"],
};
createPoll(pollParams);
// Wait for message to send, get its ID and save as @pollId
cy.get(".mx_RoomView_body .mx_EventTile").contains(".mx_EventTile[data-scroll-tokens]", pollParams.title)
.invoke("attr", "data-scroll-tokens").as("pollId");
cy.get(".mx_RoomView_body .mx_EventTile")
.contains(".mx_EventTile[data-scroll-tokens]", pollParams.title)
.invoke("attr", "data-scroll-tokens")
.as("pollId");
cy.get<string>("@pollId").then(pollId => {
cy.get<string>("@pollId").then((pollId) => {
// Bot votes 'Maybe' in the poll
botVoteForOption(bot, roomId, pollId, pollParams.options[2]);
// wait for bot's vote to arrive
cy.get('.mx_MPollBody_totalVotes').should('contain', '1 vote cast');
cy.get(".mx_MPollBody_totalVotes").should("contain", "1 vote cast");
// Open context menu
getPollTile(pollId).rightclick();
// Select edit item
cy.get('.mx_ContextualMenu').within(() => {
cy.get(".mx_ContextualMenu").within(() => {
cy.get('[aria-label="Edit"]').click();
});
// Expect error dialog
cy.get('.mx_ErrorDialog');
cy.get(".mx_ErrorDialog");
});
});
it("should be displayed correctly in thread panel", () => {
let botBob: MatrixClient;
let botCharlie: MatrixClient;
cy.getBot(synapse, { displayName: "BotBob" }).then(_bot => {
cy.getBot(synapse, { displayName: "BotBob" }).then((_bot) => {
botBob = _bot;
});
cy.getBot(synapse, { displayName: "BotCharlie" }).then(_bot => {
cy.getBot(synapse, { displayName: "BotCharlie" }).then((_bot) => {
botCharlie = _bot;
});
let roomId: string;
cy.createRoom({}).then(_roomId => {
cy.createRoom({}).then((_roomId) => {
roomId = _roomId;
cy.inviteUser(roomId, botBob.getUserId());
cy.inviteUser(roomId, botCharlie.getUserId());
cy.visit('/#/room/' + roomId);
cy.visit("/#/room/" + roomId);
// wait until the bots joined
cy.contains(".mx_TextualEvent", "and one other were invited and joined").should("exist");
});
@@ -272,16 +275,17 @@ describe("Polls", () => {
});
const pollParams = {
title: 'Does the polls feature work?',
options: ['Yes', 'No', 'Maybe'],
title: "Does the polls feature work?",
options: ["Yes", "No", "Maybe"],
};
createPoll(pollParams);
// Wait for message to send, get its ID and save as @pollId
cy.contains(".mx_RoomView_body .mx_EventTile[data-scroll-tokens]", pollParams.title)
.invoke("attr", "data-scroll-tokens").as("pollId");
.invoke("attr", "data-scroll-tokens")
.as("pollId");
cy.get<string>("@pollId").then(pollId => {
cy.get<string>("@pollId").then((pollId) => {
// Bob starts thread on the poll
botBob.sendMessage(roomId, pollId, {
body: "Hello there",
@@ -297,22 +301,22 @@ describe("Polls", () => {
botVoteForOption(botCharlie, roomId, pollId, pollParams.options[1]);
// no votes shown until I vote, check votes have arrived in main tl
cy.get('.mx_RoomView_body .mx_MPollBody_totalVotes').should('contain', '2 votes cast');
cy.get(".mx_RoomView_body .mx_MPollBody_totalVotes").should("contain", "2 votes cast");
// and thread view
cy.get('.mx_ThreadView .mx_MPollBody_totalVotes').should('contain', '2 votes cast');
cy.get(".mx_ThreadView .mx_MPollBody_totalVotes").should("contain", "2 votes cast");
cy.get('.mx_RoomView_body').within(() => {
cy.get(".mx_RoomView_body").within(() => {
// vote 'Maybe' in the main timeline poll
getPollOption(pollId, pollParams.options[2]).click('topLeft');
getPollOption(pollId, pollParams.options[2]).click("topLeft");
// both me and bob have voted Maybe
expectPollOptionVoteCount(pollId, pollParams.options[2], 2);
});
cy.get('.mx_ThreadView').within(() => {
cy.get(".mx_ThreadView").within(() => {
// votes updated in thread view too
expectPollOptionVoteCount(pollId, pollParams.options[2], 2);
// change my vote to 'Yes'
getPollOption(pollId, pollParams.options[0]).click('topLeft');
getPollOption(pollId, pollParams.options[0]).click("topLeft");
});
// Bob updates vote to 'No'
@@ -329,11 +333,11 @@ describe("Polls", () => {
};
// check counts are correct in main timeline tile
cy.get('.mx_RoomView_body').within(() => {
cy.get(".mx_RoomView_body").within(() => {
expectVoteCounts();
});
// and in thread view tile
cy.get('.mx_ThreadView').within(() => {
cy.get(".mx_ThreadView").within(() => {
expectVoteCounts();
});
});

View File

@@ -24,7 +24,7 @@ describe("Registration", () => {
beforeEach(() => {
cy.stubDefaultServer();
cy.visit("/#/register");
cy.startSynapse("consent").then(data => {
cy.startSynapse("consent").then((data) => {
synapse = data;
});
});
@@ -45,7 +45,7 @@ describe("Registration", () => {
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_ServerPickerDialog").should("not.exist");
cy.get("#mx_RegistrationForm_username").should("be.visible");
// Hide the server text as it contains the randomly allocated Synapse port
@@ -75,12 +75,14 @@ describe("Registration", () => {
cy.checkA11y();
cy.get(".mx_UseCaseSelection_skip .mx_AccessibleButton").click();
cy.url().should('contain', '/#/home');
cy.url().should("contain", "/#/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");
cy.get(".mx_DevicesPanel_myDevice .mx_DevicesPanel_deviceTrust .mx_E2EIcon").should(
"have.class",
"mx_E2EIcon_verified",
);
});
it("should require username to fulfil requirements and be available", () => {
@@ -89,7 +91,7 @@ describe("Registration", () => {
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_ServerPickerDialog").should("not.exist");
cy.get("#mx_RegistrationForm_username").should("be.visible");

View File

@@ -22,7 +22,7 @@ describe("Pills", () => {
let synapse: SynapseInstance;
beforeEach(() => {
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Sally");
@@ -33,7 +33,7 @@ describe("Pills", () => {
cy.stopSynapse(synapse);
});
it('should navigate clicks internally to the app', () => {
it("should navigate clicks internally to the app", () => {
const messageRoom = "Send Messages Here";
const targetLocalpart = "aliasssssssssssss";
cy.createRoom({
@@ -43,34 +43,35 @@ describe("Pills", () => {
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}`);
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();
// 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();
// find the pill in the timeline and click it
cy.get(".mx_EventTile_body .mx_Pill").click();
const localUrl = `/#/room/#${targetLocalpart}:`;
// verify we landed at a sane place
cy.url().should("contain", localUrl);
const localUrl = `/#/room/#${targetLocalpart}:`;
// verify we landed at a sane place
cy.url().should("contain", localUrl);
cy.wait(250); // let the room list settle
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", localUrl);
});
// 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", localUrl);
},
);
});
});

View File

@@ -46,7 +46,7 @@ describe("RightPanel", () => {
let synapse: SynapseInstance;
beforeEach(() => {
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, NAME).then(() =>
cy.window({ log: false }).then(() => {

View File

@@ -23,7 +23,7 @@ describe("Room Directory", () => {
let synapse: SynapseInstance;
beforeEach(() => {
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Ray");
@@ -36,7 +36,7 @@ describe("Room Directory", () => {
});
it("should allow admin to add alias & publish room to directory", () => {
cy.window({ log: false }).then(win => {
cy.window({ log: false }).then((win) => {
cy.createRoom({
name: "Gaming",
preset: win.matrixcs.Preset.PublicChat,
@@ -56,16 +56,14 @@ describe("Room Directory", () => {
// 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()
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]) => {
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);
@@ -75,10 +73,7 @@ describe("Room Directory", () => {
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]) => {
cy.all([cy.window({ log: false }), cy.get<MatrixClient>("@bot")]).then(([win, bot]) => {
bot.createRoom({
visibility: win.matrixcs.Visibility.Public,
name,
@@ -89,16 +84,17 @@ describe("Room Directory", () => {
cy.get('[role="button"][aria-label="Explore rooms"]').click();
cy.get('.mx_SpotlightDialog [aria-label="Search"]').type("Unknown Room");
cy.get(".mx_SpotlightDialog .mx_SpotlightDialog_otherSearches_messageSearchText")
.should("contain", "can't find the room you're looking for");
cy.get(".mx_SpotlightDialog .mx_SpotlightDialog_otherSearches_messageSearchText").should(
"contain",
"can't find the room you're looking for",
);
cy.get(".mx_SpotlightDialog_wrapper").percySnapshotElement("Room Directory - filtered no results");
cy.get('.mx_SpotlightDialog [aria-label="Search"]').type("{selectAll}{backspace}test1234");
cy.contains(".mx_SpotlightDialog .mx_SpotlightDialog_result_publicRoomName", name)
.should("exist");
cy.contains(".mx_SpotlightDialog .mx_SpotlightDialog_result_publicRoomName", name).should("exist");
cy.get(".mx_SpotlightDialog_wrapper").percySnapshotElement("Room Directory - filtered one result");
cy.get(".mx_SpotlightDialog .mx_SpotlightDialog_option").find(".mx_AccessibleButton").contains("Join").click();
cy.url().should('contain', `/#/room/#test1234:localhost`);
cy.url().should("contain", `/#/room/#test1234:localhost`);
});
});

View File

@@ -25,17 +25,20 @@ describe("Device manager", () => {
beforeEach(() => {
cy.enableLabsFeature("feature_new_device_manager");
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Alice").then(credentials => {
user = credentials;
}).then(() => {
// create some extra sessions to manage
return cy.loginUser(synapse, user.username, user.password);
}).then(() => {
return cy.loginUser(synapse, user.username, user.password);
});
cy.initTestUser(synapse, "Alice")
.then((credentials) => {
user = credentials;
})
.then(() => {
// create some extra sessions to manage
return cy.loginUser(synapse, user.username, user.password);
})
.then(() => {
return cy.loginUser(synapse, user.username, user.password);
});
});
});
@@ -45,72 +48,74 @@ describe("Device manager", () => {
it("should display sessions", () => {
cy.openUserSettings("Sessions");
cy.contains('Current session').should('exist');
cy.contains("Current session").should("exist");
cy.get('[data-testid="current-session-section"]').within(() => {
cy.contains('Unverified session').should('exist');
cy.contains("Unverified session").should("exist");
});
// current session details opened
cy.get('[data-testid="current-session-toggle-details"]').click();
cy.contains('Session details').should('exist');
cy.contains("Session details").should("exist");
// close current session details
cy.get('[data-testid="current-session-toggle-details"]').click();
cy.contains('Session details').should('not.exist');
cy.contains("Session details").should("not.exist");
cy.get('[data-testid="security-recommendations-section"]').within(() => {
cy.contains('Security recommendations').should('exist');
cy.get('[data-testid="unverified-devices-cta"]').should('have.text', 'View all (3)').click();
cy.contains("Security recommendations").should("exist");
cy.get('[data-testid="unverified-devices-cta"]').should("have.text", "View all (3)").click();
});
/**
* Other sessions section
*/
cy.contains('Other sessions').should('exist');
cy.contains("Other sessions").should("exist");
// filter applied after clicking through from security recommendations
cy.get('[aria-label="Filter devices"]').should('have.text', 'Show: Unverified');
cy.get('.mx_FilteredDeviceList_list').find('.mx_FilteredDeviceList_listItem').should('have.length', 3);
cy.get('[aria-label="Filter devices"]').should("have.text", "Show: Unverified");
cy.get(".mx_FilteredDeviceList_list").find(".mx_FilteredDeviceList_listItem").should("have.length", 3);
// select two sessions
cy.get('.mx_FilteredDeviceList_list .mx_FilteredDeviceList_listItem .mx_Checkbox').first().click();
cy.get('.mx_FilteredDeviceList_list .mx_FilteredDeviceList_listItem .mx_Checkbox').last().click();
cy.get(".mx_FilteredDeviceList_list .mx_FilteredDeviceList_listItem .mx_Checkbox").first().click();
cy.get(".mx_FilteredDeviceList_list .mx_FilteredDeviceList_listItem .mx_Checkbox").last().click();
// sign out from list selection action buttons
cy.get('[data-testid="sign-out-selection-cta"]').click();
cy.get('[data-testid="dialog-primary-button"]').click();
// list updated after sign out
cy.get('.mx_FilteredDeviceList_list').find('.mx_FilteredDeviceList_listItem').should('have.length', 1);
cy.get(".mx_FilteredDeviceList_list").find(".mx_FilteredDeviceList_listItem").should("have.length", 1);
// security recommendation count updated
cy.get('[data-testid="unverified-devices-cta"]').should('have.text', 'View all (1)');
cy.get('[data-testid="unverified-devices-cta"]').should("have.text", "View all (1)");
const sessionName = `Alice's device`;
// open the first session
cy.get('.mx_FilteredDeviceList_list .mx_FilteredDeviceList_listItem').first().within(() => {
cy.get('[aria-label="Show details"]').click();
cy.get(".mx_FilteredDeviceList_list .mx_FilteredDeviceList_listItem")
.first()
.within(() => {
cy.get('[aria-label="Show details"]').click();
cy.contains('Session details').should('exist');
cy.contains("Session details").should("exist");
cy.get('[data-testid="device-heading-rename-cta"]').click();
cy.get('[data-testid="device-rename-input"]').type(sessionName);
cy.get('[data-testid="device-rename-submit-cta"]').click();
// there should be a spinner while device updates
cy.get(".mx_Spinner").should("exist");
// wait for spinner to complete
cy.get(".mx_Spinner").should("not.exist");
cy.get('[data-testid="device-heading-rename-cta"]').click();
cy.get('[data-testid="device-rename-input"]').type(sessionName);
cy.get('[data-testid="device-rename-submit-cta"]').click();
// there should be a spinner while device updates
cy.get(".mx_Spinner").should("exist");
// wait for spinner to complete
cy.get(".mx_Spinner").should("not.exist");
// session name updated in details
cy.get('.mx_DeviceDetailHeading h3').should('have.text', sessionName);
// and main list item
cy.get('.mx_DeviceTile h4').should('have.text', sessionName);
// session name updated in details
cy.get(".mx_DeviceDetailHeading h3").should("have.text", sessionName);
// and main list item
cy.get(".mx_DeviceTile h4").should("have.text", sessionName);
// sign out using the device details sign out
cy.get('[data-testid="device-detail-sign-out-cta"]').click();
});
// sign out using the device details sign out
cy.get('[data-testid="device-detail-sign-out-cta"]').click();
});
// confirm the signout
cy.get('[data-testid="dialog-primary-button"]').click();
// no other sessions or security recommendations sections when only one session
cy.contains('Other sessions').should('not.exist');
cy.get('[data-testid="security-recommendations-section"]').should('not.exist');
cy.contains("Other sessions").should("not.exist");
cy.get('[data-testid="security-recommendations-section"]').should("not.exist");
});
});

View File

@@ -21,7 +21,7 @@ import { SynapseInstance } from "../../plugins/synapsedocker";
function seedLabs(synapse: SynapseInstance, labsVal: boolean | null): void {
cy.initTestUser(synapse, "Sally", () => {
// seed labs flag
cy.window({ log: false }).then(win => {
cy.window({ log: false }).then((win) => {
if (typeof labsVal === "boolean") {
// stringify boolean
win.localStorage.setItem("mx_labs_feature_feature_hidden_read_receipts", `${labsVal}`);
@@ -64,7 +64,7 @@ describe("Hidden Read Receipts Setting Migration", () => {
let synapse: SynapseInstance;
beforeEach(() => {
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
});
});
@@ -73,17 +73,17 @@ describe("Hidden Read Receipts Setting Migration", () => {
cy.stopSynapse(synapse);
});
it('should not migrate the lack of a labs flag', () => {
it("should not migrate the lack of a labs flag", () => {
seedLabs(synapse, null);
testForVal(null);
});
it('should migrate labsHiddenRR=false as sendRR=true', () => {
it("should migrate labsHiddenRR=false as sendRR=true", () => {
seedLabs(synapse, false);
testForVal(true);
});
it('should migrate labsHiddenRR=true as sendRR=false', () => {
it("should migrate labsHiddenRR=true as sendRR=false", () => {
seedLabs(synapse, true);
testForVal(false);
});

View File

@@ -26,18 +26,17 @@ import { ProxyInstance } from "../../plugins/sliding-sync";
describe("Sliding Sync", () => {
beforeEach(() => {
cy.startSynapse("default").as("synapse").then(synapse => {
cy.startProxy(synapse).as("proxy");
});
cy.startSynapse("default")
.as("synapse")
.then((synapse) => {
cy.startProxy(synapse).as("proxy");
});
cy.all([
cy.get<SynapseInstance>("@synapse"),
cy.get<ProxyInstance>("@proxy"),
]).then(([synapse, proxy]) => {
cy.all([cy.get<SynapseInstance>("@synapse"), cy.get<ProxyInstance>("@proxy")]).then(([synapse, proxy]) => {
cy.enableLabsFeature("feature_sliding_sync");
cy.intercept("/config.json?cachebuster=*", req => {
return req.continue(res => {
cy.intercept("/config.json?cachebuster=*", (req) => {
return req.continue((res) => {
res.send(200, {
...res.body,
setting_defaults: {
@@ -62,11 +61,16 @@ describe("Sliding Sync", () => {
// assert order
const checkOrder = (wantOrder: string[]) => {
cy.contains(".mx_RoomSublist", "Rooms").find(".mx_RoomTile_title").should((elements) => {
expect(_.map(elements, (e) => {
return e.textContent;
}), "rooms are sorted").to.deep.equal(wantOrder);
});
cy.contains(".mx_RoomSublist", "Rooms")
.find(".mx_RoomTile_title")
.should((elements) => {
expect(
_.map(elements, (e) => {
return e.textContent;
}),
"rooms are sorted",
).to.deep.equal(wantOrder);
});
};
const bumpRoom = (alias: string) => {
// Send a message into the given room, this should bump the room to the top
@@ -80,9 +84,11 @@ describe("Sliding Sync", () => {
const createAndJoinBob = () => {
// create a Bob user
cy.get<SynapseInstance>("@synapse").then((synapse) => {
return cy.getBot(synapse, {
displayName: "Bob",
}).as("bob");
return cy
.getBot(synapse, {
displayName: "Bob",
})
.as("bob");
});
// invite Bob to Test Room and accept then send a message.
@@ -95,7 +101,7 @@ describe("Sliding Sync", () => {
// sanity check everything works
it("should correctly render expected messages", () => {
cy.get<string>("@roomId").then(roomId => cy.visit("/#/room/" + roomId));
cy.get<string>("@roomId").then((roomId) => cy.visit("/#/room/" + roomId));
cy.setSettingValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
// Wait until configuration is finished
@@ -114,54 +120,52 @@ describe("Sliding Sync", () => {
cy.createRoom({ name: "Pineapple" }).then(() => cy.contains(".mx_RoomSublist", "Pineapple"));
cy.createRoom({ name: "Orange" }).then(() => cy.contains(".mx_RoomSublist", "Orange"));
// check the rooms are in the right order
cy.get(".mx_RoomTile").should('have.length', 4); // due to the Test Room in beforeEach
checkOrder([
"Orange", "Pineapple", "Apple", "Test Room",
]);
cy.get(".mx_RoomTile").should("have.length", 4); // due to the Test Room in beforeEach
checkOrder(["Orange", "Pineapple", "Apple", "Test Room"]);
cy.contains(".mx_RoomSublist", "Rooms").find(".mx_RoomSublist_menuButton").click({ force: true });
cy.contains("A-Z").click();
cy.get('.mx_StyledRadioButton_checked').should("contain.text", "A-Z");
checkOrder([
"Apple", "Orange", "Pineapple", "Test Room",
]);
cy.get(".mx_StyledRadioButton_checked").should("contain.text", "A-Z");
checkOrder(["Apple", "Orange", "Pineapple", "Test Room"]);
});
it("should move rooms around as new events arrive", () => {
// create rooms and check room names are correct
cy.createRoom({ name: "Apple" }).as("roomA").then(() => cy.contains(".mx_RoomSublist", "Apple"));
cy.createRoom({ name: "Pineapple" }).as("roomP").then(() => cy.contains(".mx_RoomSublist", "Pineapple"));
cy.createRoom({ name: "Orange" }).as("roomO").then(() => cy.contains(".mx_RoomSublist", "Orange"));
cy.createRoom({ name: "Apple" })
.as("roomA")
.then(() => cy.contains(".mx_RoomSublist", "Apple"));
cy.createRoom({ name: "Pineapple" })
.as("roomP")
.then(() => cy.contains(".mx_RoomSublist", "Pineapple"));
cy.createRoom({ name: "Orange" })
.as("roomO")
.then(() => cy.contains(".mx_RoomSublist", "Orange"));
// Select the Test Room
cy.contains(".mx_RoomTile", "Test Room").click();
checkOrder([
"Orange", "Pineapple", "Apple", "Test Room",
]);
checkOrder(["Orange", "Pineapple", "Apple", "Test Room"]);
bumpRoom("@roomA");
checkOrder([
"Apple", "Orange", "Pineapple", "Test Room",
]);
checkOrder(["Apple", "Orange", "Pineapple", "Test Room"]);
bumpRoom("@roomO");
checkOrder([
"Orange", "Apple", "Pineapple", "Test Room",
]);
checkOrder(["Orange", "Apple", "Pineapple", "Test Room"]);
bumpRoom("@roomO");
checkOrder([
"Orange", "Apple", "Pineapple", "Test Room",
]);
checkOrder(["Orange", "Apple", "Pineapple", "Test Room"]);
bumpRoom("@roomP");
checkOrder([
"Pineapple", "Orange", "Apple", "Test Room",
]);
checkOrder(["Pineapple", "Orange", "Apple", "Test Room"]);
});
it("should not move the selected room: it should be sticky", () => {
// create rooms and check room names are correct
cy.createRoom({ name: "Apple" }).as("roomA").then(() => cy.contains(".mx_RoomSublist", "Apple"));
cy.createRoom({ name: "Pineapple" }).as("roomP").then(() => cy.contains(".mx_RoomSublist", "Pineapple"));
cy.createRoom({ name: "Orange" }).as("roomO").then(() => cy.contains(".mx_RoomSublist", "Orange"));
cy.createRoom({ name: "Apple" })
.as("roomA")
.then(() => cy.contains(".mx_RoomSublist", "Apple"));
cy.createRoom({ name: "Pineapple" })
.as("roomP")
.then(() => cy.contains(".mx_RoomSublist", "Pineapple"));
cy.createRoom({ name: "Orange" })
.as("roomO")
.then(() => cy.contains(".mx_RoomSublist", "Orange"));
// Given a list of Orange, Pineapple, Apple - if Pineapple is active and a message is sent in Apple, the list should
// turn into Apple, Pineapple, Orange - the index position of Pineapple never changes even though the list should technically
@@ -169,23 +173,17 @@ describe("Sliding Sync", () => {
// Select the Pineapple room
cy.contains(".mx_RoomTile", "Pineapple").click();
checkOrder([
"Orange", "Pineapple", "Apple", "Test Room",
]);
checkOrder(["Orange", "Pineapple", "Apple", "Test Room"]);
// Move Apple
bumpRoom("@roomA");
checkOrder([
"Apple", "Pineapple", "Orange", "Test Room",
]);
checkOrder(["Apple", "Pineapple", "Orange", "Test Room"]);
// Select the Test Room
cy.contains(".mx_RoomTile", "Test Room").click();
// the rooms reshuffle to match reality
checkOrder([
"Apple", "Orange", "Pineapple", "Test Room",
]);
checkOrder(["Apple", "Orange", "Pineapple", "Test Room"]);
});
it("should show the right unread notifications", () => {
@@ -212,7 +210,8 @@ describe("Sliding Sync", () => {
cy.contains(".mx_RoomTile", "Test Room").should("not.have.class", "mx_NotificationBadge_count");
});
it("should not show unread indicators", () => { // TODO: for now. Later we should.
it("should not show unread indicators", () => {
// TODO: for now. Later we should.
createAndJoinBob();
// disable notifs in this room (TODO: CS API call?)
@@ -223,17 +222,13 @@ describe("Sliding Sync", () => {
cy.createRoom({
name: "Dummy",
});
checkOrder([
"Dummy", "Test Room",
]);
checkOrder(["Dummy", "Test Room"]);
cy.all([cy.get<string>("@roomId"), cy.get<MatrixClient>("@bob")]).then(([roomId, bob]) => {
return bob.sendTextMessage(roomId, "Do you read me?");
});
// wait for this message to arrive, tell by the room list resorting
checkOrder([
"Test Room", "Dummy",
]);
checkOrder(["Test Room", "Dummy"]);
cy.contains(".mx_RoomTile", "Test Room").get(".mx_NotificationBadge").should("not.exist");
});
@@ -242,13 +237,18 @@ describe("Sliding Sync", () => {
cy.get(".mx_UserMenu_userAvatar").click();
cy.contains("All settings").click();
cy.contains("Preferences").click();
cy.contains(".mx_SettingsFlag", "Show timestamps in 12 hour format").should("exist").find(
".mx_ToggleSwitch_on").should("not.exist");
cy.contains(".mx_SettingsFlag", "Show timestamps in 12 hour format").should("exist").find(
".mx_ToggleSwitch_ball").click();
cy.contains(".mx_SettingsFlag", "Show timestamps in 12 hour format", { timeout: 2000 }).should("exist").find(
".mx_ToggleSwitch_on", { timeout: 2000 },
).should("exist");
cy.contains(".mx_SettingsFlag", "Show timestamps in 12 hour format")
.should("exist")
.find(".mx_ToggleSwitch_on")
.should("not.exist");
cy.contains(".mx_SettingsFlag", "Show timestamps in 12 hour format")
.should("exist")
.find(".mx_ToggleSwitch_ball")
.click();
cy.contains(".mx_SettingsFlag", "Show timestamps in 12 hour format", { timeout: 2000 })
.should("exist")
.find(".mx_ToggleSwitch_on", { timeout: 2000 })
.should("exist");
});
it("should show and be able to accept/reject/rescind invites", () => {
@@ -263,50 +263,56 @@ describe("Sliding Sync", () => {
// - roomJoin: will join this room
// - roomReject: will reject the invite
// - roomRescind: will make Bob rescind the invite
let roomJoin; let roomReject; let roomRescind; let bobClient;
cy.get<MatrixClient>("@bob").then((bob) => {
bobClient = bob;
return Promise.all([
bob.createRoom({ name: "Join" }),
bob.createRoom({ name: "Reject" }),
bob.createRoom({ name: "Rescind" }),
]);
}).then(([join, reject, rescind]) => {
roomJoin = join.room_id;
roomReject = reject.room_id;
roomRescind = rescind.room_id;
return Promise.all([
bobClient.invite(roomJoin, clientUserId),
bobClient.invite(roomReject, clientUserId),
bobClient.invite(roomRescind, clientUserId),
]);
});
let roomJoin;
let roomReject;
let roomRescind;
let bobClient;
cy.get<MatrixClient>("@bob")
.then((bob) => {
bobClient = bob;
return Promise.all([
bob.createRoom({ name: "Join" }),
bob.createRoom({ name: "Reject" }),
bob.createRoom({ name: "Rescind" }),
]);
})
.then(([join, reject, rescind]) => {
roomJoin = join.room_id;
roomReject = reject.room_id;
roomRescind = rescind.room_id;
return Promise.all([
bobClient.invite(roomJoin, clientUserId),
bobClient.invite(roomReject, clientUserId),
bobClient.invite(roomRescind, clientUserId),
]);
});
// wait for them all to be on the UI
cy.get(".mx_RoomTile").should('have.length', 4); // due to the Test Room in beforeEach
cy.get(".mx_RoomTile").should("have.length", 4); // due to the Test Room in beforeEach
cy.contains(".mx_RoomTile", "Join").click();
cy.contains(".mx_AccessibleButton", "Accept").click();
checkOrder([
"Join", "Test Room",
]);
checkOrder(["Join", "Test Room"]);
cy.contains(".mx_RoomTile", "Reject").click();
cy.contains(".mx_RoomView .mx_AccessibleButton", "Reject").click();
// wait for the rejected room to disappear
cy.get(".mx_RoomTile").should('have.length', 3);
cy.get(".mx_RoomTile").should("have.length", 3);
// check the lists are correct
checkOrder([
"Join", "Test Room",
]);
cy.contains(".mx_RoomSublist", "Invites").find(".mx_RoomTile_title").should((elements) => {
expect(_.map(elements, (e) => {
return e.textContent;
}), "rooms are sorted").to.deep.equal(["Rescind"]);
});
checkOrder(["Join", "Test Room"]);
cy.contains(".mx_RoomSublist", "Invites")
.find(".mx_RoomTile_title")
.should((elements) => {
expect(
_.map(elements, (e) => {
return e.textContent;
}),
"rooms are sorted",
).to.deep.equal(["Rescind"]);
});
// now rescind the invite
cy.get<MatrixClient>("@bob").then((bob) => {
@@ -314,19 +320,19 @@ describe("Sliding Sync", () => {
});
// wait for the rescind to take effect and check the joined list once more
cy.get(".mx_RoomTile").should('have.length', 2);
checkOrder([
"Join", "Test Room",
]);
cy.get(".mx_RoomTile").should("have.length", 2);
checkOrder(["Join", "Test Room"]);
});
it("should show a favourite DM only in the favourite sublist", () => {
cy.createRoom({
name: "Favourite DM",
is_direct: true,
}).as("room").then(roomId => {
cy.getClient().then(cli => cli.setRoomTag(roomId, "m.favourite", { order: 0.5 }));
});
})
.as("room")
.then((roomId) => {
cy.getClient().then((cli) => cli.setRoomTag(roomId, "m.favourite", { order: 0.5 }));
});
cy.contains('.mx_RoomSublist[aria-label="Favourites"] .mx_RoomTile', "Favourite DM").should("exist");
cy.contains('.mx_RoomSublist[aria-label="People"] .mx_RoomTile', "Favourite DM").should("not.exist");
@@ -335,7 +341,9 @@ describe("Sliding Sync", () => {
// Regression test for a bug in SS mode, but would be useful to have in non-SS mode too.
// This ensures we are setting RoomViewStore state correctly.
it("should clear the reply to field when swapping rooms", () => {
cy.createRoom({ name: "Other Room" }).as("roomA").then(() => cy.contains(".mx_RoomSublist", "Other Room"));
cy.createRoom({ name: "Other Room" })
.as("roomA")
.then(() => cy.contains(".mx_RoomSublist", "Other Room"));
cy.get<string>("@roomId").then((roomId) => {
return cy.sendEvent(roomId, null, "m.room.message", {
body: "Hello world",
@@ -346,9 +354,9 @@ describe("Sliding Sync", () => {
cy.contains(".mx_RoomTile", "Test Room").click();
cy.get(".mx_ReplyPreview").should("not.exist");
// click reply-to on the Hello World message
cy.contains(".mx_EventTile", "Hello world").find('.mx_AccessibleButton[aria-label="Reply"]').click(
{ force: true },
);
cy.contains(".mx_EventTile", "Hello world")
.find('.mx_AccessibleButton[aria-label="Reply"]')
.click({ force: true });
// check it's visible
cy.get(".mx_ReplyPreview").should("exist");
// now click Other Room
@@ -365,28 +373,31 @@ describe("Sliding Sync", () => {
it("should not cancel replies when permalinks are clicked ", () => {
cy.get<string>("@roomId").then((roomId) => {
// we require a first message as you cannot click the permalink text with the avatar in the way
return cy.sendEvent(roomId, null, "m.room.message", {
body: "First message",
msgtype: "m.text",
}).then(() => {
return cy.sendEvent(roomId, null, "m.room.message", {
body: "Permalink me",
return cy
.sendEvent(roomId, null, "m.room.message", {
body: "First message",
msgtype: "m.text",
})
.then(() => {
return cy.sendEvent(roomId, null, "m.room.message", {
body: "Permalink me",
msgtype: "m.text",
});
})
.then(() => {
cy.sendEvent(roomId, null, "m.room.message", {
body: "Reply to me",
msgtype: "m.text",
});
});
}).then(() => {
cy.sendEvent(roomId, null, "m.room.message", {
body: "Reply to me",
msgtype: "m.text",
});
});
});
// select the room
cy.contains(".mx_RoomTile", "Test Room").click();
cy.get(".mx_ReplyPreview").should("not.exist");
// click reply-to on the Reply to me message
cy.contains(".mx_EventTile", "Reply to me").find('.mx_AccessibleButton[aria-label="Reply"]').click(
{ force: true },
);
cy.contains(".mx_EventTile", "Reply to me")
.find('.mx_AccessibleButton[aria-label="Reply"]')
.click({ force: true });
// check it's visible
cy.get(".mx_ReplyPreview").should("exist");
// now click on the permalink for Permalink me

View File

@@ -37,12 +37,14 @@ function spaceCreateOptions(spaceName: string): ICreateRoomOpts {
creation_content: {
type: "m.space",
},
initial_state: [{
type: "m.room.name",
content: {
name: spaceName,
initial_state: [
{
type: "m.room.name",
content: {
name: spaceName,
},
},
}],
],
};
}
@@ -61,10 +63,10 @@ describe("Spaces", () => {
let user: UserCredentials;
beforeEach(() => {
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Sue").then(_user => {
cy.initTestUser(synapse, "Sue").then((_user) => {
user = _user;
cy.mockClipboard();
});
@@ -78,8 +80,10 @@ describe("Spaces", () => {
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('.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!");
@@ -108,8 +112,10 @@ describe("Spaces", () => {
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('.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...");
@@ -145,8 +151,10 @@ describe("Spaces", () => {
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('.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}");
@@ -163,7 +171,7 @@ describe("Spaces", () => {
it("should allow user to invite another to a space", () => {
let bot: MatrixClient;
cy.getBot(synapse, { displayName: "BotBob" }).then(_bot => {
cy.getBot(synapse, { displayName: "BotBob" }).then((_bot) => {
bot = _bot;
});
@@ -198,13 +206,17 @@ describe("Spaces", () => {
});
cy.getSpacePanelButton("My Space").should("exist");
cy.getBot(synapse, { displayName: "BotBob" }).then({ timeout: 10000 }, async bot => {
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
cy.getSpacePanelButton("Space Space").should("exist")
.parent().next().find('.mx_SpaceButton[aria-label="My Space"]').should("exist");
cy.getSpacePanelButton("Space Space")
.should("exist")
.parent()
.next()
.find('.mx_SpaceButton[aria-label="My Space"]')
.should("exist");
});
it("should include rooms in space home", () => {
@@ -216,16 +228,10 @@ describe("Spaces", () => {
}).as("roomId2");
const spaceName = "Spacey Mc. Space Space";
cy.all([
cy.get<string>("@roomId1"),
cy.get<string>("@roomId2"),
]).then(([roomId1, roomId2]) => {
cy.all([cy.get<string>("@roomId1"), cy.get<string>("@roomId2")]).then(([roomId1, roomId2]) => {
cy.createSpace({
name: spaceName,
initial_state: [
spaceChildInitialState(roomId1),
spaceChildInitialState(roomId2),
],
initial_state: [spaceChildInitialState(roomId1), spaceChildInitialState(roomId2)],
}).as("spaceId");
});
@@ -244,12 +250,10 @@ describe("Spaces", () => {
cy.createSpace({
name: "Child Space",
initial_state: [],
}).then(spaceId => {
}).then((spaceId) => {
cy.createSpace({
name: "Root Space",
initial_state: [
spaceChildInitialState(spaceId),
],
initial_state: [spaceChildInitialState(spaceId)],
}).as("spaceId");
});
cy.get('.mx_SpacePanel .mx_SpaceButton[aria-label="Root Space"]').should("exist");
@@ -258,7 +262,7 @@ describe("Spaces", () => {
const axeOptions = {
rules: {
// Disable this check as it triggers on nested roving tab index elements which are in practice fine
'nested-interactive': {
"nested-interactive": {
enabled: false,
},
},
@@ -269,8 +273,10 @@ describe("Spaces", () => {
cy.get(".mx_SpaceButton_toggleCollapse").click({ force: true });
cy.get(".mx_SpacePanel:not(.collapsed)").should("exist");
cy.contains(".mx_SpaceItem", "Root Space").should("exist")
.contains(".mx_SpaceItem", "Child Space").should("exist");
cy.contains(".mx_SpaceItem", "Root Space")
.should("exist")
.contains(".mx_SpaceItem", "Child Space")
.should("exist");
cy.checkA11y(undefined, axeOptions);
cy.get(".mx_SpacePanel").percySnapshotElement("Space panel expanded", { widths: [258] });

View File

@@ -26,7 +26,7 @@ import Shadow = Cypress.Shadow;
export enum Filter {
People = "people",
PublicRooms = "public_rooms"
PublicRooms = "public_rooms",
}
declare global {
@@ -37,78 +37,86 @@ declare global {
* Opens the spotlight dialog
*/
openSpotlightDialog(
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>,
): Chainable<JQuery<HTMLElement>>;
spotlightDialog(
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>,
): Chainable<JQuery<HTMLElement>>;
spotlightFilter(
filter: Filter | null,
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>,
): Chainable<JQuery<HTMLElement>>;
spotlightSearch(
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>,
): Chainable<JQuery<HTMLElement>>;
spotlightResults(
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>,
): Chainable<JQuery<HTMLElement>>;
roomHeaderName(
options?: Partial<Loggable & Timeoutable & Withinable & Shadow>
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(
"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(
"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(
"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(
"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(
"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(
"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(() => {
@@ -121,9 +129,7 @@ Cypress.Commands.add("startDM", (name: string) => {
cy.spotlightResults().eq(0).click();
});
// send first message to start DM
cy.get(".mx_BasicMessageComposer_input")
.should("have.focus")
.type("Hey!{enter}");
cy.get(".mx_BasicMessageComposer_input").should("have.focus").type("Hey!{enter}");
// The DM room is created at this point, this can take a little bit of time
cy.contains(".mx_EventTile_body", "Hey!", { timeout: 30000 });
cy.contains(".mx_RoomSublist[aria-label=People]", name);
@@ -148,46 +154,52 @@ describe("Spotlight", () => {
let room3Id: string;
beforeEach(() => {
cy.startSynapse("default").then(data => {
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;
bot1.joinRoom(room1Id);
cy.visit("/#/room/" + room1Id);
});
bot2.createRoom({ name: room2Name, visibility: Visibility.Public })
.then(({ room_id: _room2Id }) => {
room2Id = _room2Id;
bot2.invite(room2Id, bot1.getUserId());
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;
bot1.joinRoom(room1Id);
cy.visit("/#/room/" + room1Id);
});
bot2.createRoom({
name: room3Name,
visibility: Visibility.Public, initial_state: [{
type: "m.room.history_visibility",
state_key: "",
content: {
history_visibility: "world_readable",
bot2.createRoom({ name: room2Name, visibility: Visibility.Public }).then(
({ room_id: _room2Id }) => {
room2Id = _room2Id;
bot2.invite(room2Id, bot1.getUserId());
},
}],
}).then(({ room_id: _room3Id }) => {
room3Id = _room3Id;
bot2.invite(room3Id, bot1.getUserId());
});
}),
).then(() =>
cy.get('.mx_RoomSublist_skeletonUI').should('not.exist'),
);
);
bot2.createRoom({
name: room3Name,
visibility: Visibility.Public,
initial_state: [
{
type: "m.room.history_visibility",
state_key: "",
content: {
history_visibility: "world_readable",
},
},
],
}).then(({ room_id: _room3Id }) => {
room3Id = _room3Id;
bot2.invite(room3Id, bot1.getUserId());
});
}),
)
.then(() => cy.get(".mx_RoomSublist_skeletonUI").should("not.exist"));
});
});
@@ -216,63 +228,71 @@ describe("Spotlight", () => {
});
it("should find joined rooms", () => {
cy.openSpotlightDialog().within(() => {
cy.spotlightSearch().clear().type(room1Name);
cy.wait(3000); // 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();
cy.url().should("contain", room1Id);
}).then(() => {
cy.roomHeaderName().should("contain", room1Name);
});
cy.openSpotlightDialog()
.within(() => {
cy.spotlightSearch().clear().type(room1Name);
cy.wait(3000); // 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();
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.wait(3000); // 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");
cy.spotlightResults().eq(0).click();
cy.url().should("contain", room1Id);
}).then(() => {
cy.roomHeaderName().should("contain", room1Name);
});
cy.openSpotlightDialog()
.within(() => {
cy.spotlightFilter(Filter.PublicRooms);
cy.spotlightSearch().clear().type(room1Name);
cy.wait(3000); // 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");
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.wait(3000); // 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");
cy.spotlightResults().eq(0).click();
cy.url().should("contain", room2Id);
}).then(() => {
cy.get(".mx_RoomView_MessageList").should("have.length", 1);
cy.roomHeaderName().should("contain", room2Name);
});
cy.openSpotlightDialog()
.within(() => {
cy.spotlightFilter(Filter.PublicRooms);
cy.spotlightSearch().clear().type(room2Name);
cy.wait(3000); // 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");
cy.spotlightResults().eq(0).click();
cy.url().should("contain", room2Id);
})
.then(() => {
cy.get(".mx_RoomView_MessageList").should("have.length", 1);
cy.roomHeaderName().should("contain", room2Name);
});
});
it("should find unknown public world readable rooms", () => {
cy.openSpotlightDialog().within(() => {
cy.spotlightFilter(Filter.PublicRooms);
cy.spotlightSearch().clear().type(room3Name);
cy.wait(3000); // 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");
cy.spotlightResults().eq(0).click();
cy.url().should("contain", room3Id);
}).then(() => {
cy.get(".mx_RoomPreviewBar_actions .mx_AccessibleButton").click();
cy.roomHeaderName().should("contain", room3Name);
});
cy.openSpotlightDialog()
.within(() => {
cy.spotlightFilter(Filter.PublicRooms);
cy.spotlightSearch().clear().type(room3Name);
cy.wait(3000); // 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");
cy.spotlightResults().eq(0).click();
cy.url().should("contain", room3Id);
})
.then(() => {
cy.get(".mx_RoomPreviewBar_actions .mx_AccessibleButton").click();
cy.roomHeaderName().should("contain", room3Name);
});
});
// TODO: We currently cant test finding rooms on other homeservers/other protocols
@@ -299,29 +319,33 @@ describe("Spotlight", () => {
});
*/
it("should find known people", () => {
cy.openSpotlightDialog().within(() => {
cy.spotlightFilter(Filter.People);
cy.spotlightSearch().clear().type(bot1Name);
cy.wait(3000); // 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();
}).then(() => {
cy.roomHeaderName().should("contain", bot1Name);
});
cy.openSpotlightDialog()
.within(() => {
cy.spotlightFilter(Filter.People);
cy.spotlightSearch().clear().type(bot1Name);
cy.wait(3000); // 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();
})
.then(() => {
cy.roomHeaderName().should("contain", bot1Name);
});
});
it("should find unknown people", () => {
cy.openSpotlightDialog().within(() => {
cy.spotlightFilter(Filter.People);
cy.spotlightSearch().clear().type(bot2Name);
cy.wait(3000); // 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();
}).then(() => {
cy.roomHeaderName().should("contain", bot2Name);
});
cy.openSpotlightDialog()
.within(() => {
cy.spotlightFilter(Filter.People);
cy.spotlightSearch().clear().type(bot2Name);
cy.wait(3000); // 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();
})
.then(() => {
cy.roomHeaderName().should("contain", bot2Name);
});
});
it("should find group DMs by usernames or user ids", () => {
@@ -340,10 +364,7 @@ describe("Spotlight", () => {
// Send first message to actually start DM
cy.roomHeaderName().should("contain", bot2Name);
cy.get(".mx_BasicMessageComposer_input")
.click()
.should("have.focus")
.type("Hey!{enter}");
cy.get(".mx_BasicMessageComposer_input").click().should("have.focus").type("Hey!{enter}");
// Assert DM exists by checking for the first message and the room being in the room list
cy.contains(".mx_EventTile_body", "Hey!", { timeout: 30000 });
@@ -352,13 +373,13 @@ describe("Spotlight", () => {
// Invite BotBob into existing DM with ByteBot
cy.getDmRooms(bot2.getUserId())
.should("have.length", 1)
.then(dmRooms => cy.getClient().then(client => client.getRoom(dmRooms[0])))
.then(groupDm => {
.then((dmRooms) => cy.getClient().then((client) => client.getRoom(dmRooms[0])))
.then((groupDm) => {
cy.inviteUser(groupDm.roomId, bot1.getUserId());
cy.roomHeaderName().should(($element) =>
expect($element.get(0).innerText).contains(groupDm.name));
cy.roomHeaderName().should(($element) => expect($element.get(0).innerText).contains(groupDm.name));
cy.get(".mx_RoomSublist[aria-label=People]").should(($element) =>
expect($element.get(0).innerText).contains(groupDm.name));
expect($element.get(0).innerText).contains(groupDm.name),
);
// Search for BotBob by id, should return group DM and user
cy.openSpotlightDialog().within(() => {
@@ -407,17 +428,19 @@ describe("Spotlight", () => {
});
it("should allow opening group chat dialog", () => {
cy.openSpotlightDialog().within(() => {
cy.spotlightFilter(Filter.People);
cy.spotlightSearch().clear().type(bot2Name);
cy.wait(3000); // 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");
cy.get(".mx_SpotlightDialog_startGroupChat").click();
}).then(() => {
cy.get('[role=dialog]').should("contain", "Direct Messages");
});
cy.openSpotlightDialog()
.within(() => {
cy.spotlightFilter(Filter.People);
cy.spotlightSearch().clear().type(bot2Name);
cy.wait(3000); // 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");
cy.get(".mx_SpotlightDialog_startGroupChat").click();
})
.then(() => {
cy.get("[role=dialog]").should("contain", "Direct Messages");
});
});
it("should close spotlight after starting a DM", () => {
@@ -445,38 +468,40 @@ describe("Spotlight", () => {
// our debouncing logic only starts the search after a short timeout,
// so we wait a few milliseconds.
cy.wait(1000);
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.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");
});
});
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");
});
});
});
});
});

View File

@@ -21,7 +21,7 @@ import { MatrixClient } from "../../global";
function markWindowBeforeReload(): void {
// mark our window object to "know" when it gets reloaded
cy.window().then(w => w.beforeReload = true);
cy.window().then((w) => (w.beforeReload = true));
}
describe("Threads", () => {
@@ -30,10 +30,10 @@ describe("Threads", () => {
beforeEach(() => {
// Default threads to ON for this spec
cy.enableLabsFeature("feature_thread");
cy.window().then(win => {
cy.window().then((win) => {
win.localStorage.setItem("mx_lhs_size", "0"); // Collapse left panel for these tests
});
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Tom");
@@ -78,12 +78,12 @@ describe("Threads", () => {
cy.getBot(synapse, {
displayName: "BotBob",
autoAcceptInvites: false,
}).then(_bot => {
}).then((_bot) => {
bot = _bot;
});
let roomId: string;
cy.createRoom({}).then(_roomId => {
cy.createRoom({}).then((_roomId) => {
roomId = _roomId;
cy.inviteUser(roomId, bot.getUserId());
bot.joinRoom(roomId);
@@ -95,10 +95,11 @@ describe("Threads", () => {
// Wait for message to send, get its ID and save as @threadId
cy.contains(".mx_RoomView_body .mx_EventTile[data-scroll-tokens]", "Hello Mr. Bot")
.invoke("attr", "data-scroll-tokens").as("threadId");
.invoke("attr", "data-scroll-tokens")
.as("threadId");
// Bot starts thread
cy.get<string>("@threadId").then(threadId => {
cy.get<string>("@threadId").then((threadId) => {
bot.sendMessage(roomId, threadId, {
body: "Hello there",
msgtype: "m.text",
@@ -119,7 +120,8 @@ describe("Threads", () => {
// User reacts to message instead
cy.contains(".mx_ThreadView .mx_EventTile .mx_EventTile_line", "Hello there")
.find('[aria-label="React"]').click({ force: true }); // Cypress has no ability to hover
.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.contains('[role="menuitem"]', "👋").click();
@@ -127,7 +129,8 @@ describe("Threads", () => {
// User redacts their prior response
cy.contains(".mx_ThreadView .mx_EventTile .mx_EventTile_line", "Test")
.find('[aria-label="Options"]').click({ force: true }); // Cypress has no ability to hover
.find('[aria-label="Options"]')
.click({ force: true }); // Cypress has no ability to hover
cy.get(".mx_IconizedContextMenu").within(() => {
cy.contains('[role="menuitem"]', "Remove").click();
});
@@ -144,7 +147,7 @@ describe("Threads", () => {
cy.get(".mx_ThreadPanel .mx_BaseCard_close").click();
// Bot responds to thread
cy.get<string>("@threadId").then(threadId => {
cy.get<string>("@threadId").then((threadId) => {
bot.sendMessage(roomId, threadId, {
body: "How are things?",
msgtype: "m.text",
@@ -178,45 +181,55 @@ describe("Threads", () => {
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?");
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");
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");
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 => {
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",
body: "I'm very good thanks :)",
msgtype: "m.text",
},
"m.relates_to": {
"rel_type": "m.replace",
"event_id": eventId,
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 :)");
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_content").should(
"contain",
"I'm very good thanks :)",
);
});
it("can send voice messages", () => {
@@ -227,7 +240,7 @@ describe("Threads", () => {
});
let roomId: string;
cy.createRoom({}).then(_roomId => {
cy.createRoom({}).then((_roomId) => {
roomId = _roomId;
cy.visit("/#/room/" + roomId);
});
@@ -237,7 +250,9 @@ describe("Threads", () => {
// Create thread
cy.contains(".mx_RoomView_body .mx_EventTile[data-scroll-tokens]", "Hello Mr. Bot")
.realHover().find(".mx_MessageActionBar_threadButton").click();
.realHover()
.find(".mx_MessageActionBar_threadButton")
.click();
cy.get(".mx_ThreadView_timelinePanelWrapper").should("have.length", 1);
cy.openMessageComposerOptions(true).find(`[aria-label="Voice Message"]`).click();
@@ -250,7 +265,7 @@ describe("Threads", () => {
it("right panel behaves correctly", () => {
// Create room
let roomId: string;
cy.createRoom({}).then(_roomId => {
cy.createRoom({}).then((_roomId) => {
roomId = _roomId;
cy.visit("/#/room/" + roomId);
});
@@ -259,7 +274,9 @@ describe("Threads", () => {
// Create thread
cy.contains(".mx_RoomView_body .mx_EventTile[data-scroll-tokens]", "Hello Mr. Bot")
.realHover().find(".mx_MessageActionBar_threadButton").click();
.realHover()
.find(".mx_MessageActionBar_threadButton")
.click();
cy.get(".mx_ThreadView_timelinePanelWrapper").should("have.length", 1);
// Send message to thread
@@ -271,7 +288,9 @@ describe("Threads", () => {
// Open existing thread
cy.contains(".mx_RoomView_body .mx_EventTile[data-scroll-tokens]", "Hello Mr. Bot")
.realHover().find(".mx_MessageActionBar_threadButton").click();
.realHover()
.find(".mx_MessageActionBar_threadButton")
.click();
cy.get(".mx_ThreadView_timelinePanelWrapper").should("have.length", 1);
cy.get(".mx_BaseCard .mx_EventTile").should("contain", "Hello Mr. Bot");
cy.get(".mx_BaseCard .mx_EventTile").should("contain", "Hello Mr. User");

View File

@@ -45,10 +45,7 @@ const expectDisplayName = (e: JQuery<HTMLElement>, displayName: string): void =>
};
const expectAvatar = (e: JQuery<HTMLElement>, avatarUrl: string): void => {
cy.all([
cy.window({ log: false }),
cy.getClient(),
]).then(([win, cli]) => {
cy.all([cy.window({ log: false }), cy.getClient()]).then(([win, cli]) => {
const size = AVATAR_SIZE * win.devicePixelRatio;
expect(e.find(".mx_BaseAvatar_image").attr("src")).to.equal(
// eslint-disable-next-line no-restricted-properties
@@ -75,10 +72,10 @@ describe("Timeline", () => {
let newAvatarUrl: string;
beforeEach(() => {
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, OLD_NAME).then(() =>
cy.createRoom({ name: ROOM_NAME }).then(_room1Id => {
cy.createRoom({ name: ROOM_NAME }).then((_room1Id) => {
roomId = _room1Id;
}),
);
@@ -154,8 +151,11 @@ describe("Timeline", () => {
it("should create and configure a room on IRC layout", () => {
cy.visit("/#/room/" + roomId);
cy.setSettingValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
cy.contains(".mx_RoomView_body .mx_GenericEventListSummary[data-layout=irc] " +
".mx_GenericEventListSummary_summary", "created and configured the room.").should("exist");
cy.contains(
".mx_RoomView_body .mx_GenericEventListSummary[data-layout=irc] " +
".mx_GenericEventListSummary_summary",
"created and configured the room.",
).should("exist");
cy.get(".mx_Spinner").should("not.exist");
cy.percySnapshot("Configured room on IRC layout");
});
@@ -165,8 +165,10 @@ describe("Timeline", () => {
cy.setSettingValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
// Wait until configuration is finished
cy.contains(".mx_RoomView_body .mx_GenericEventListSummary " +
".mx_GenericEventListSummary_summary", "created and configured the room.").should("exist");
cy.contains(
".mx_RoomView_body .mx_GenericEventListSummary " + ".mx_GenericEventListSummary_summary",
"created and configured the room.",
).should("exist");
// Click "expand" link button
cy.get(".mx_GenericEventListSummary_toggle[aria-expanded=false]").click();
@@ -177,13 +179,13 @@ describe("Timeline", () => {
// = calc(var(--name-width) + 10px + var(--icon-width))
// = 80 + 10 + 14 = 104px
cy.get(".mx_EventTile[data-layout=irc].mx_EventTile_info:first-of-type .mx_EventTile_line")
.should('have.css', "margin-inline-start", "104px")
.should('have.css', "inset-inline-start", "0px");
.should("have.css", "margin-inline-start", "104px")
.should("have.css", "inset-inline-start", "0px");
cy.get(".mx_Spinner").should("not.exist");
// Exclude timestamp from snapshot
const percyCSS = ".mx_RoomView_body .mx_EventTile_info .mx_MessageTimestamp "
+ "{ visibility: hidden !important; }";
const percyCSS =
".mx_RoomView_body .mx_EventTile_info .mx_MessageTimestamp " + "{ visibility: hidden !important; }";
cy.percySnapshot("Event line with inline start margin on IRC layout", { percyCSS });
cy.checkA11y();
});
@@ -192,8 +194,10 @@ describe("Timeline", () => {
sendEvent(roomId);
cy.visit("/#/room/" + roomId);
cy.setSettingValue("showHiddenEventsInTimeline", null, SettingLevel.DEVICE, true);
cy.contains(".mx_RoomView_body .mx_GenericEventListSummary .mx_GenericEventListSummary_summary",
"created and configured the room.").should("exist");
cy.contains(
".mx_RoomView_body .mx_GenericEventListSummary .mx_GenericEventListSummary_summary",
"created and configured the room.",
).should("exist");
// Edit message
cy.contains(".mx_RoomView_body .mx_EventTile .mx_EventTile_line", "Message").within(() => {
@@ -206,20 +210,23 @@ describe("Timeline", () => {
cy.get(".mx_RoomView_body .mx_EventTile_info .mx_MessageTimestamp").click();
// Exclude timestamp from snapshot
const percyCSS = ".mx_RoomView_body .mx_EventTile .mx_MessageTimestamp "
+ "{ visibility: hidden !important; }";
const percyCSS =
".mx_RoomView_body .mx_EventTile .mx_MessageTimestamp " + "{ visibility: hidden !important; }";
// should not add inline start padding to a hidden event line on IRC layout
cy.setSettingValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
cy.get(".mx_EventTile[data-layout=irc].mx_EventTile_info .mx_EventTile_line")
.should('have.css', 'padding-inline-start', '0px');
cy.get(".mx_EventTile[data-layout=irc].mx_EventTile_info .mx_EventTile_line").should(
"have.css",
"padding-inline-start",
"0px",
);
cy.percySnapshot("Hidden event line with zero padding on IRC layout", { percyCSS });
// should add inline start padding to a hidden event line on modern layout
cy.setSettingValue("layout", null, SettingLevel.DEVICE, Layout.Group);
cy.get(".mx_EventTile[data-layout=group].mx_EventTile_info .mx_EventTile_line")
// calc(var(--EventTile_group_line-spacing-inline-start) + 20px) = 64 + 20 = 84px
.should('have.css', 'padding-inline-start', '84px');
.should("have.css", "padding-inline-start", "84px");
cy.percySnapshot("Hidden event line with padding on modern layout", { percyCSS });
});
@@ -227,8 +234,10 @@ describe("Timeline", () => {
sendEvent(roomId);
cy.visit("/#/room/" + roomId);
cy.setSettingValue("showHiddenEventsInTimeline", null, SettingLevel.DEVICE, true);
cy.contains(".mx_RoomView_body .mx_GenericEventListSummary " +
".mx_GenericEventListSummary_summary", "created and configured the room.").should("exist");
cy.contains(
".mx_RoomView_body .mx_GenericEventListSummary " + ".mx_GenericEventListSummary_summary",
"created and configured the room.",
).should("exist");
// Edit message
cy.contains(".mx_RoomView_body .mx_EventTile .mx_EventTile_line", "Message").within(() => {
@@ -238,9 +247,12 @@ describe("Timeline", () => {
cy.contains(".mx_RoomView_body .mx_EventTile[data-scroll-tokens]", "MessageEdit").should("exist");
// Click top left of the event toggle, which should not be covered by MessageActionBar's safe area
cy.get(".mx_EventTile .mx_ViewSourceEvent").should("exist").realHover().within(() => {
cy.get(".mx_ViewSourceEvent_toggle").click('topLeft', { force: false });
});
cy.get(".mx_EventTile .mx_ViewSourceEvent")
.should("exist")
.realHover()
.within(() => {
cy.get(".mx_ViewSourceEvent_toggle").click("topLeft", { force: false });
});
// Make sure the expand toggle worked
cy.get(".mx_EventTile .mx_ViewSourceEvent_expanded .mx_ViewSourceEvent_toggle").should("be.visible");
@@ -249,8 +261,11 @@ describe("Timeline", () => {
it("should click 'collapse' link button on the first hovered info event line on bubble layout", () => {
cy.visit("/#/room/" + roomId);
cy.setSettingValue("layout", null, SettingLevel.DEVICE, Layout.Bubble);
cy.contains(".mx_RoomView_body .mx_GenericEventListSummary[data-layout=bubble] " +
".mx_GenericEventListSummary_summary", "created and configured the room.").should("exist");
cy.contains(
".mx_RoomView_body .mx_GenericEventListSummary[data-layout=bubble] " +
".mx_GenericEventListSummary_summary",
"created and configured the room.",
).should("exist");
// Click "expand" link button
cy.get(".mx_GenericEventListSummary_toggle[aria-expanded=false]").click();
@@ -340,10 +355,14 @@ describe("Timeline", () => {
cy.getComposer().type(`${reply}{enter}`);
cy.get(".mx_RoomView_body .mx_EventTile .mx_EventTile_line .mx_ReplyTile .mx_MTextBody")
.should("contain", MESSAGE);
cy.contains(".mx_RoomView_body .mx_EventTile > .mx_EventTile_line > .mx_MTextBody", reply)
.should("have.length", 1);
cy.get(".mx_RoomView_body .mx_EventTile .mx_EventTile_line .mx_ReplyTile .mx_MTextBody").should(
"contain",
MESSAGE,
);
cy.contains(".mx_RoomView_body .mx_EventTile > .mx_EventTile_line > .mx_MTextBody", reply).should(
"have.length",
1,
);
});
it("can reply with a voice message", () => {
@@ -355,10 +374,14 @@ describe("Timeline", () => {
cy.wait(3000);
cy.get(".mx_RoomView_body .mx_MessageComposer .mx_MessageComposer_sendMessage").click();
cy.get(".mx_RoomView_body .mx_EventTile .mx_EventTile_line .mx_ReplyTile .mx_MTextBody")
.should("contain", MESSAGE);
cy.get(".mx_RoomView_body .mx_EventTile > .mx_EventTile_line > .mx_MVoiceMessageBody")
.should("have.length", 1);
cy.get(".mx_RoomView_body .mx_EventTile .mx_EventTile_line .mx_ReplyTile .mx_MTextBody").should(
"contain",
MESSAGE,
);
cy.get(".mx_RoomView_body .mx_EventTile > .mx_EventTile_line > .mx_MVoiceMessageBody").should(
"have.length",
1,
);
});
});
});

View File

@@ -47,15 +47,15 @@ describe("Analytics Toast", () => {
});
it("should not show an analytics toast if config has nothing about posthog", () => {
cy.intercept("/config.json?cachebuster=*", req => {
req.continue(res => {
cy.intercept("/config.json?cachebuster=*", (req) => {
req.continue((res) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { posthog, ...body } = res.body;
res.send(200, body);
});
});
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Tod");
});
@@ -66,8 +66,8 @@ describe("Analytics Toast", () => {
describe("with posthog enabled", () => {
beforeEach(() => {
cy.intercept("/config.json?cachebuster=*", req => {
req.continue(res => {
cy.intercept("/config.json?cachebuster=*", (req) => {
req.continue((res) => {
res.send(200, {
...res.body,
posthog: {
@@ -78,7 +78,7 @@ describe("Analytics Toast", () => {
});
});
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Tod");
rejectToast("Notifications");

View File

@@ -22,7 +22,7 @@ describe("Update", () => {
let synapse: SynapseInstance;
beforeEach(() => {
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
});
});
@@ -45,9 +45,11 @@ describe("Update", () => {
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);
});
cy.url()
.should("contain", "updated=" + NEW_VERSION)
.then((href) => {
const url = new URL(href);
expect(url.searchParams.get("updated")).to.equal(NEW_VERSION);
});
});
});

View File

@@ -24,10 +24,10 @@ describe("User Menu", () => {
let user: UserCredentials;
beforeEach(() => {
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Jeff").then(credentials => {
cy.initTestUser(synapse, "Jeff").then((credentials) => {
user = credentials;
});
});

View File

@@ -26,23 +26,23 @@ describe("User Onboarding (new user)", () => {
let bot1: MatrixClient;
beforeEach(() => {
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Jane Doe");
cy.window({ log: false }).then(win => {
cy.window({ log: false }).then((win) => {
win.localStorage.setItem("mx_registration_time", "1656633601");
});
cy.reload().then(() => {
// wait for the app to load
return cy.get(".mx_MatrixChat", { timeout: 15000 });
});
cy.getBot(synapse, { displayName: bot1Name }).then(_bot1 => {
cy.getBot(synapse, { displayName: bot1Name }).then((_bot1) => {
bot1 = _bot1;
});
cy.get('.mx_UserOnboardingPage').should('exist');
cy.get('.mx_UserOnboardingButton').should('exist');
cy.get('.mx_UserOnboardingList')
.should('exist')
cy.get(".mx_UserOnboardingPage").should("exist");
cy.get(".mx_UserOnboardingButton").should("exist");
cy.get(".mx_UserOnboardingList")
.should("exist")
.should(($list) => {
const list = $list.get(0);
expect(getComputedStyle(list).opacity).to.be.eq("1");
@@ -55,44 +55,42 @@ describe("User Onboarding (new user)", () => {
});
it("page is shown and preference exists", () => {
cy.get('.mx_UserOnboardingPage')
.percySnapshotElement("User onboarding page");
cy.get(".mx_UserOnboardingPage").percySnapshotElement("User onboarding page");
cy.openUserSettings("Preferences");
cy.contains("Show shortcut to welcome checklist above the room list").should("exist");
});
it("app download dialog", () => {
cy.contains(".mx_UserOnboardingTask_action", "Download apps").click();
cy.get('[role=dialog]')
.contains("#mx_BaseDialog_title", "Download Element")
.should("exist");
cy.get('[role=dialog]')
.percySnapshotElement("App download dialog", {
widths: [640],
});
cy.get("[role=dialog]").contains("#mx_BaseDialog_title", "Download Element").should("exist");
cy.get("[role=dialog]").percySnapshotElement("App download dialog", {
widths: [640],
});
});
it("using find friends action should increase progress", () => {
cy.get(".mx_ProgressBar").invoke("val").then((oldProgress) => {
const findPeopleAction = cy.contains(".mx_UserOnboardingTask_action", "Find friends");
expect(findPeopleAction).to.exist;
findPeopleAction.click();
cy.get(".mx_InviteDialog_editor input").type(bot1.getUserId());
cy.get(".mx_InviteDialog_buttonAndSpinner").click();
cy.get(".mx_InviteDialog_buttonAndSpinner").should("not.exist");
const message = "Hi!";
cy.get(".mx_SendMessageComposer").type(`${message}!{enter}`);
cy.contains(".mx_MTextBody.mx_EventTile_content", message);
cy.visit("/#/home");
cy.get('.mx_UserOnboardingPage').should('exist');
cy.get('.mx_UserOnboardingButton').should('exist');
cy.get('.mx_UserOnboardingList')
.should('exist')
.should(($list) => {
const list = $list.get(0);
expect(getComputedStyle(list).opacity).to.be.eq("1");
});
cy.get(".mx_ProgressBar").invoke("val").should("be.greaterThan", oldProgress);
});
cy.get(".mx_ProgressBar")
.invoke("val")
.then((oldProgress) => {
const findPeopleAction = cy.contains(".mx_UserOnboardingTask_action", "Find friends");
expect(findPeopleAction).to.exist;
findPeopleAction.click();
cy.get(".mx_InviteDialog_editor input").type(bot1.getUserId());
cy.get(".mx_InviteDialog_buttonAndSpinner").click();
cy.get(".mx_InviteDialog_buttonAndSpinner").should("not.exist");
const message = "Hi!";
cy.get(".mx_SendMessageComposer").type(`${message}!{enter}`);
cy.contains(".mx_MTextBody.mx_EventTile_content", message);
cy.visit("/#/home");
cy.get(".mx_UserOnboardingPage").should("exist");
cy.get(".mx_UserOnboardingButton").should("exist");
cy.get(".mx_UserOnboardingList")
.should("exist")
.should(($list) => {
const list = $list.get(0);
expect(getComputedStyle(list).opacity).to.be.eq("1");
});
cy.get(".mx_ProgressBar").invoke("val").should("be.greaterThan", oldProgress);
});
});
});

View File

@@ -22,10 +22,10 @@ describe("User Onboarding (old user)", () => {
let synapse: SynapseInstance;
beforeEach(() => {
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Jane Doe");
cy.window({ log: false }).then(win => {
cy.window({ log: false }).then((win) => {
win.localStorage.setItem("mx_registration_time", "2");
});
cy.reload().then(() => {
@@ -41,8 +41,8 @@ describe("User Onboarding (old user)", () => {
});
it("page and preference are hidden", () => {
cy.get('.mx_UserOnboardingPage').should('not.exist');
cy.get('.mx_UserOnboardingButton').should('not.exist');
cy.get(".mx_UserOnboardingPage").should("not.exist");
cy.get(".mx_UserOnboardingButton").should("not.exist");
cy.openUserSettings("Preferences");
cy.contains("Show shortcut to welcome page above the room list").should("not.exist");
});

View File

@@ -23,7 +23,7 @@ describe("UserView", () => {
let synapse: SynapseInstance;
beforeEach(() => {
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Violet");
@@ -36,7 +36,7 @@ describe("UserView", () => {
});
it("should render the user view as expected", () => {
cy.get<MatrixClient>("@bot").then(bot => {
cy.get<MatrixClient>("@bot").then((bot) => {
cy.visit(`/#/user/${bot.getUserId()}`);
});

View File

@@ -19,7 +19,7 @@ import { IWidget } from "matrix-widget-api";
import { SynapseInstance } from "../../plugins/synapsedocker";
const ROOM_NAME = 'Test Room';
const ROOM_NAME = "Test Room";
const WIDGET_ID = "fake-widget";
const WIDGET_HTML = `
<html lang="en">
@@ -32,18 +32,18 @@ const WIDGET_HTML = `
</html>
`;
describe('Widget Layout', () => {
describe("Widget Layout", () => {
let widgetUrl: string;
let synapse: SynapseInstance;
let roomId: string;
beforeEach(() => {
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Sally");
});
cy.serveHtmlFile(WIDGET_HTML).then(url => {
cy.serveHtmlFile(WIDGET_HTML).then((url) => {
widgetUrl = url;
});
@@ -53,34 +53,38 @@ describe('Widget Layout', () => {
roomId = id;
// setup widget via state event
cy.getClient().then(async matrixClient => {
const content: IWidget = {
id: WIDGET_ID,
creatorUserId: 'somebody',
type: 'widget',
name: 'widget',
url: widgetUrl,
};
await matrixClient.sendStateEvent(roomId, 'im.vector.modular.widgets', content, WIDGET_ID);
}).as('widgetEventSent');
cy.getClient()
.then(async (matrixClient) => {
const content: IWidget = {
id: WIDGET_ID,
creatorUserId: "somebody",
type: "widget",
name: "widget",
url: widgetUrl,
};
await matrixClient.sendStateEvent(roomId, "im.vector.modular.widgets", content, WIDGET_ID);
})
.as("widgetEventSent");
// set initial layout
cy.getClient().then(async matrixClient => {
const content = {
widgets: {
[WIDGET_ID]: {
container: 'top', index: 1, width: 100, height: 0,
cy.getClient()
.then(async (matrixClient) => {
const content = {
widgets: {
[WIDGET_ID]: {
container: "top",
index: 1,
width: 100,
height: 0,
},
},
},
};
await matrixClient.sendStateEvent(roomId, 'io.element.widgets.layout', content, "");
}).as('layoutEventSent');
};
await matrixClient.sendStateEvent(roomId, "io.element.widgets.layout", content, "");
})
.as("layoutEventSent");
});
cy.all([
cy.get<string>("@widgetEventSent"),
cy.get<string>("@layoutEventSent"),
]).then(() => {
cy.all([cy.get<string>("@widgetEventSent"), cy.get<string>("@layoutEventSent")]).then(() => {
// open the room
cy.viewRoomByName(ROOM_NAME);
});
@@ -91,31 +95,34 @@ describe('Widget Layout', () => {
cy.stopWebServers();
});
it('manually resize the height of the top container layout', () => {
cy.get('iframe[title="widget"]').invoke('height').should('be.lessThan', 250);
it("manually resize the height of the top container layout", () => {
cy.get('iframe[title="widget"]').invoke("height").should("be.lessThan", 250);
cy.get('.mx_AppsContainer_resizerHandle')
.trigger('mousedown')
.trigger('mousemove', { clientX: 0, clientY: 550, force: true })
.trigger('mouseup', { clientX: 0, clientY: 550, force: true });
cy.get(".mx_AppsContainer_resizerHandle")
.trigger("mousedown")
.trigger("mousemove", { clientX: 0, clientY: 550, force: true })
.trigger("mouseup", { clientX: 0, clientY: 550, force: true });
cy.get('iframe[title="widget"]').invoke('height').should('be.greaterThan', 400);
cy.get('iframe[title="widget"]').invoke("height").should("be.greaterThan", 400);
});
it('programatically resize the height of the top container layout', () => {
cy.get('iframe[title="widget"]').invoke('height').should('be.lessThan', 250);
it("programatically resize the height of the top container layout", () => {
cy.get('iframe[title="widget"]').invoke("height").should("be.lessThan", 250);
cy.getClient().then(async matrixClient => {
cy.getClient().then(async (matrixClient) => {
const content = {
widgets: {
[WIDGET_ID]: {
container: 'top', index: 1, width: 100, height: 100,
container: "top",
index: 1,
width: 100,
height: 100,
},
},
};
await matrixClient.sendStateEvent(roomId, 'io.element.widgets.layout', content, "");
await matrixClient.sendStateEvent(roomId, "io.element.widgets.layout", content, "");
});
cy.get('iframe[title="widget"]').invoke('height').should('be.greaterThan', 400);
cy.get('iframe[title="widget"]').invoke("height").should("be.greaterThan", 400);
});
});

View File

@@ -67,8 +67,8 @@ const WIDGET_HTML = `
`;
function openStickerPicker() {
cy.get('.mx_MessageComposer_buttonMenu').click();
cy.get('#stickersButton').click();
cy.get(".mx_MessageComposer_buttonMenu").click();
cy.get("#stickersButton").click();
}
function sendStickerFromPicker() {
@@ -76,18 +76,16 @@ function sendStickerFromPicker() {
// 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();
cy.get("#sendsticker").should("exist").click();
});
// Sticker picker should close itself after sending.
cy.get(".mx_AppTileFullWidth#stickers").should('not.exist');
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}/`);
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}"]`)
@@ -107,12 +105,12 @@ describe("Stickers", () => {
let synapse: SynapseInstance;
beforeEach(() => {
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Sally");
});
cy.serveHtmlFile(WIDGET_HTML).then(url => {
cy.serveHtmlFile(WIDGET_HTML).then((url) => {
stickerPickerUrl = url;
});
});
@@ -122,7 +120,7 @@ describe("Stickers", () => {
cy.stopWebServers();
});
it('should send a sticker to multiple rooms', () => {
it("should send a sticker to multiple rooms", () => {
cy.createRoom({
name: ROOM_NAME_1,
}).as("roomId1");

View File

@@ -57,7 +57,7 @@ function waitForRoomWidget(win: Cypress.AUTWindow, widgetId: string, roomId: str
return new Promise((resolve, reject) => {
function eventsInIntendedState(evList) {
const widgetPresent = evList.some((ev) => {
return ev.getContent() && ev.getContent()['id'] === widgetId;
return ev.getContent() && ev.getContent()["id"] === widgetId;
});
if (add) {
return widgetPresent;
@@ -68,7 +68,7 @@ function waitForRoomWidget(win: Cypress.AUTWindow, widgetId: string, roomId: str
const room = matrixClient.getRoom(roomId);
const startingWidgetEvents = room.currentState.getStateEvents('im.vector.modular.widgets');
const startingWidgetEvents = room.currentState.getStateEvents("im.vector.modular.widgets");
if (eventsInIntendedState(startingWidgetEvents)) {
resolve();
return;
@@ -77,7 +77,7 @@ function waitForRoomWidget(win: Cypress.AUTWindow, widgetId: string, roomId: str
function onRoomStateEvents(ev: MatrixEvent) {
if (ev.getRoomId() !== roomId || ev.getType() !== "im.vector.modular.widgets") return;
const currentWidgetEvents = room.currentState.getStateEvents('im.vector.modular.widgets');
const currentWidgetEvents = room.currentState.getStateEvents("im.vector.modular.widgets");
if (eventsInIntendedState(currentWidgetEvents)) {
matrixClient.removeListener(win.matrixcs.RoomStateEvent.Events, onRoomStateEvents);
@@ -95,35 +95,39 @@ describe("Widget PIP", () => {
let bot: MatrixClient;
let demoWidgetUrl: string;
function roomCreateAddWidgetPip(userRemove: 'leave' | 'kick' | 'ban') {
function roomCreateAddWidgetPip(userRemove: "leave" | "kick" | "ban") {
cy.createRoom({
name: ROOM_NAME,
invite: [bot.getUserId()],
}).then(roomId => {
}).then((roomId) => {
// sets bot to Admin and user to Moderator
cy.getClient().then(matrixClient => {
return matrixClient.sendStateEvent(roomId, 'm.room.power_levels', {
users: {
[user.userId]: 50,
[bot.getUserId()]: 100,
},
});
}).as('powerLevelsChanged');
cy.getClient()
.then((matrixClient) => {
return matrixClient.sendStateEvent(roomId, "m.room.power_levels", {
users: {
[user.userId]: 50,
[bot.getUserId()]: 100,
},
});
})
.as("powerLevelsChanged");
// bot joins the room
cy.botJoinRoom(bot, roomId).as('botJoined');
cy.botJoinRoom(bot, roomId).as("botJoined");
// setup widget via state event
cy.getClient().then(async matrixClient => {
const content: IWidget = {
id: DEMO_WIDGET_ID,
creatorUserId: 'somebody',
type: DEMO_WIDGET_TYPE,
name: DEMO_WIDGET_NAME,
url: demoWidgetUrl,
};
await matrixClient.sendStateEvent(roomId, 'im.vector.modular.widgets', content, DEMO_WIDGET_ID);
}).as('widgetEventSent');
cy.getClient()
.then(async (matrixClient) => {
const content: IWidget = {
id: DEMO_WIDGET_ID,
creatorUserId: "somebody",
type: DEMO_WIDGET_TYPE,
name: DEMO_WIDGET_NAME,
url: demoWidgetUrl,
};
await matrixClient.sendStateEvent(roomId, "im.vector.modular.widgets", content, DEMO_WIDGET_ID);
})
.as("widgetEventSent");
// open the room
cy.viewRoomByName(ROOM_NAME);
@@ -133,7 +137,7 @@ describe("Widget PIP", () => {
cy.get<string>("@botJoined"),
cy.get<string>("@widgetEventSent"),
]).then(() => {
cy.window().then(async win => {
cy.window().then(async (win) => {
// wait for widget state event
await waitForRoomWidget(win, DEMO_WIDGET_ID, roomId, true);
@@ -145,21 +149,23 @@ describe("Widget PIP", () => {
// checks that widget is opened in pip
cy.accessIframe(`iframe[title="${DEMO_WIDGET_NAME}"]`).within({}, () => {
cy.get("#demo").should('exist').then(async () => {
const userId = user.userId;
if (userRemove == 'leave') {
cy.getClient().then(async matrixClient => {
await matrixClient.leave(roomId);
});
} else if (userRemove == 'kick') {
await bot.kick(roomId, userId);
} else if (userRemove == 'ban') {
await bot.ban(roomId, userId);
}
cy.get("#demo")
.should("exist")
.then(async () => {
const userId = user.userId;
if (userRemove == "leave") {
cy.getClient().then(async (matrixClient) => {
await matrixClient.leave(roomId);
});
} else if (userRemove == "kick") {
await bot.kick(roomId, userId);
} else if (userRemove == "ban") {
await bot.ban(roomId, userId);
}
// checks that pip window is closed
cy.get(".mx_LegacyCallView_pip").should("not.exist");
});
// checks that pip window is closed
cy.get(".mx_LegacyCallView_pip").should("not.exist");
});
});
});
});
@@ -167,17 +173,17 @@ describe("Widget PIP", () => {
}
beforeEach(() => {
cy.startSynapse("default").then(data => {
cy.startSynapse("default").then((data) => {
synapse = data;
cy.initTestUser(synapse, "Mike").then(_user => {
cy.initTestUser(synapse, "Mike").then((_user) => {
user = _user;
});
cy.getBot(synapse, { displayName: "Bot", autoAcceptInvites: false }).then(_bot => {
cy.getBot(synapse, { displayName: "Bot", autoAcceptInvites: false }).then((_bot) => {
bot = _bot;
});
});
cy.serveHtmlFile(DEMO_WIDGET_HTML).then(url => {
cy.serveHtmlFile(DEMO_WIDGET_HTML).then((url) => {
demoWidgetUrl = url;
});
});
@@ -187,15 +193,15 @@ describe("Widget PIP", () => {
cy.stopWebServers();
});
it('should be closed on leave', () => {
roomCreateAddWidgetPip('leave');
it("should be closed on leave", () => {
roomCreateAddWidgetPip("leave");
});
it('should be closed on kick', () => {
roomCreateAddWidgetPip('kick');
it("should be closed on kick", () => {
roomCreateAddWidgetPip("kick");
});
it('should be closed on ban', () => {
roomCreateAddWidgetPip('ban');
it("should be closed on ban", () => {
roomCreateAddWidgetPip("ban");
});
});

View File

@@ -1,39 +1,39 @@
{
"versions": [
"r0.0.1",
"r0.1.0",
"r0.2.0",
"r0.3.0",
"r0.4.0",
"r0.5.0",
"r0.6.0",
"r0.6.1",
"v1.1",
"v1.2",
"v1.3",
"v1.4"
],
"unstable_features": {
"org.matrix.label_based_filtering": true,
"org.matrix.e2e_cross_signing": true,
"org.matrix.msc2432": true,
"uk.half-shot.msc2666.mutual_rooms": true,
"io.element.e2ee_forced.public": false,
"io.element.e2ee_forced.private": false,
"io.element.e2ee_forced.trusted_private": false,
"org.matrix.msc3026.busy_presence": false,
"org.matrix.msc2285.stable": true,
"org.matrix.msc3827.stable": true,
"org.matrix.msc2716": false,
"org.matrix.msc3030": false,
"org.matrix.msc3440.stable": true,
"org.matrix.msc3771": true,
"org.matrix.msc3773": false,
"fi.mau.msc2815": false,
"org.matrix.msc3882": false,
"org.matrix.msc3881": false,
"org.matrix.msc3874": false,
"org.matrix.msc3886": false,
"org.matrix.msc3912": false
}
}
"versions": [
"r0.0.1",
"r0.1.0",
"r0.2.0",
"r0.3.0",
"r0.4.0",
"r0.5.0",
"r0.6.0",
"r0.6.1",
"v1.1",
"v1.2",
"v1.3",
"v1.4"
],
"unstable_features": {
"org.matrix.label_based_filtering": true,
"org.matrix.e2e_cross_signing": true,
"org.matrix.msc2432": true,
"uk.half-shot.msc2666.mutual_rooms": true,
"io.element.e2ee_forced.public": false,
"io.element.e2ee_forced.private": false,
"io.element.e2ee_forced.trusted_private": false,
"org.matrix.msc3026.busy_presence": false,
"org.matrix.msc2285.stable": true,
"org.matrix.msc3827.stable": true,
"org.matrix.msc2716": false,
"org.matrix.msc3030": false,
"org.matrix.msc3440.stable": true,
"org.matrix.msc3771": true,
"org.matrix.msc3773": false,
"fi.mau.msc2815": false,
"org.matrix.msc3882": false,
"org.matrix.msc3881": false,
"org.matrix.msc3874": false,
"org.matrix.msc3886": false,
"org.matrix.msc3912": false
}
}

View File

@@ -42,7 +42,8 @@ export function dockerRun(opts: {
const args = [
"run",
"--name", `${opts.containerName}-${crypto.randomBytes(4).toString("hex")}`,
"--name",
`${opts.containerName}-${crypto.randomBytes(4).toString("hex")}`,
"-d",
...params,
opts.image,
@@ -58,23 +59,22 @@ export function dockerRun(opts: {
});
}
export function dockerExec(args: {
containerId: string;
params: string[];
}): Promise<void> {
export function dockerExec(args: { containerId: string; params: string[] }): Promise<void> {
return new Promise<void>((resolve, reject) => {
childProcess.execFile("docker", [
"exec", args.containerId,
...args.params,
], { encoding: 'utf8' }, (err, stdout, stderr) => {
if (err) {
console.log(stdout);
console.log(stderr);
reject(err);
return;
}
resolve();
});
childProcess.execFile(
"docker",
["exec", args.containerId, ...args.params],
{ encoding: "utf8" },
(err, stdout, stderr) => {
if (err) {
console.log(stdout);
console.log(stderr);
reject(err);
return;
}
resolve();
},
);
});
}
@@ -87,58 +87,45 @@ export async function dockerLogs(args: {
const stderrFile = args.stderrFile ? await fse.open(args.stderrFile, "w") : "ignore";
await new Promise<void>((resolve) => {
childProcess.spawn("docker", [
"logs",
args.containerId,
], {
stdio: ["ignore", stdoutFile, stderrFile],
}).once('close', resolve);
childProcess
.spawn("docker", ["logs", args.containerId], {
stdio: ["ignore", stdoutFile, stderrFile],
})
.once("close", resolve);
});
if (args.stdoutFile) await fse.close(<number>stdoutFile);
if (args.stderrFile) await fse.close(<number>stderrFile);
}
export function dockerStop(args: {
containerId: string;
}): Promise<void> {
export function dockerStop(args: { containerId: string }): Promise<void> {
return new Promise<void>((resolve, reject) => {
childProcess.execFile('docker', [
"stop",
args.containerId,
], err => {
childProcess.execFile("docker", ["stop", args.containerId], (err) => {
if (err) reject(err);
resolve();
});
});
}
export function dockerRm(args: {
containerId: string;
}): Promise<void> {
export function dockerRm(args: { containerId: string }): Promise<void> {
return new Promise<void>((resolve, reject) => {
childProcess.execFile('docker', [
"rm",
args.containerId,
], err => {
childProcess.execFile("docker", ["rm", args.containerId], (err) => {
if (err) reject(err);
resolve();
});
});
}
export function dockerIp(args: {
containerId: string;
}): Promise<string> {
export function dockerIp(args: { containerId: string }): Promise<string> {
return new Promise<string>((resolve, reject) => {
childProcess.execFile('docker', [
"inspect",
"-f", "{{ .NetworkSettings.IPAddress }}",
args.containerId,
], (err, stdout) => {
if (err) reject(err);
else resolve(stdout.trim());
});
childProcess.execFile(
"docker",
["inspect", "-f", "{{ .NetworkSettings.IPAddress }}", args.containerId],
(err, stdout) => {
if (err) reject(err);
else resolve(stdout.trim());
},
);
});
}

View File

@@ -27,7 +27,7 @@ import { log } from "./log";
/**
* @type {Cypress.PluginConfig}
*/
export default function(on: PluginEvents, config: PluginConfigOptions) {
export default function (on: PluginEvents, config: PluginConfigOptions) {
docker(on, config);
synapseDocker(on, config);
slidingSyncProxyDocker(on, config);

View File

@@ -41,10 +41,7 @@ async function proxyStart(dockerTag: string, synapse: SynapseInstance): Promise<
const postgresId = await dockerRun({
image: "postgres",
containerName: "react-sdk-cypress-sliding-sync-postgres",
params: [
"--rm",
"-e", `POSTGRES_PASSWORD=${PG_PASSWORD}`,
],
params: ["--rm", "-e", `POSTGRES_PASSWORD=${PG_PASSWORD}`],
});
const postgresIp = await dockerIp({ containerId: postgresId });
@@ -54,14 +51,11 @@ async function proxyStart(dockerTag: string, synapse: SynapseInstance): Promise<
const waitTimeMillis = 30000;
const startTime = new Date().getTime();
let lastErr: Error;
while ((new Date().getTime() - startTime) < waitTimeMillis) {
while (new Date().getTime() - startTime < waitTimeMillis) {
try {
await dockerExec({
containerId: postgresId,
params: [
"pg_isready",
"-U", "postgres",
],
params: ["pg_isready", "-U", "postgres"],
});
lastErr = null;
break;
@@ -82,10 +76,14 @@ async function proxyStart(dockerTag: string, synapse: SynapseInstance): Promise<
containerName: "react-sdk-cypress-sliding-sync-proxy",
params: [
"--rm",
"-p", `${port}:8008/tcp`,
"-e", "SYNCV3_SECRET=bwahahaha",
"-e", `SYNCV3_SERVER=http://${synapseIp}:8008`,
"-e", `SYNCV3_DB=user=postgres dbname=postgres password=${PG_PASSWORD} host=${postgresIp} sslmode=disable`,
"-p",
`${port}:8008/tcp`,
"-e",
"SYNCV3_SECRET=bwahahaha",
"-e",
`SYNCV3_SERVER=http://${synapseIp}:8008`,
"-e",
`SYNCV3_DB=user=postgres dbname=postgres password=${PG_PASSWORD} host=${postgresIp} sslmode=disable`,
],
});
console.log(new Date(), "started!");

View File

@@ -54,11 +54,11 @@ async function cfgDirFromTemplate(template: string): Promise<SynapseConfig> {
if (!stats?.isDirectory) {
throw new Error(`No such template: ${template}`);
}
const tempDir = await fse.mkdtemp(path.join(os.tmpdir(), 'react-sdk-synapsedocker-'));
const tempDir = await fse.mkdtemp(path.join(os.tmpdir(), "react-sdk-synapsedocker-"));
// copy the contents of the template dir, omitting homeserver.yaml as we'll template that
console.log(`Copy ${templateDir} -> ${tempDir}`);
await fse.copy(templateDir, tempDir, { filter: f => path.basename(f) !== 'homeserver.yaml' });
await fse.copy(templateDir, tempDir, { filter: (f) => path.basename(f) !== "homeserver.yaml" });
const registrationSecret = randB64Bytes(16);
const macaroonSecret = randB64Bytes(16);
@@ -102,11 +102,7 @@ async function synapseStart(template: string): Promise<SynapseInstance> {
const synapseId = await dockerRun({
image: "matrixdotorg/synapse:develop",
containerName: `react-sdk-cypress-synapse`,
params: [
"--rm",
"-v", `${synCfg.configDir}:/data`,
"-p", `${synCfg.port}:8008/tcp`,
],
params: ["--rm", "-v", `${synCfg.configDir}:/data`, "-p", `${synCfg.port}:8008/tcp`],
cmd: "run",
});
@@ -117,9 +113,12 @@ async function synapseStart(template: string): Promise<SynapseInstance> {
containerId: synapseId,
params: [
"curl",
"--connect-timeout", "30",
"--retry", "30",
"--retry-delay", "1",
"--connect-timeout",
"30",
"--retry",
"30",
"--retry-delay",
"1",
"--retry-all-errors",
"--silent",
"http://localhost:8008/health",

View File

@@ -5,21 +5,21 @@ pid_file: /data/homeserver.pid
public_baseurl: http://localhost:8008/
# Listener is always port 8008 (configured in the container)
listeners:
- port: 8008
tls: false
bind_addresses: ['::']
type: http
x_forwarded: true
- port: 8008
tls: false
bind_addresses: ["::"]
type: http
x_forwarded: true
resources:
- names: [client, federation, consent]
compress: false
resources:
- names: [client, federation, consent]
compress: false
# An sqlite in-memory database is fast & automatically wipes each time
database:
name: "sqlite3"
args:
database: ":memory:"
name: "sqlite3"
args:
database: ":memory:"
# Needs to be configured to log to the console like a good docker process
log_config: "/data/log.config"
@@ -27,19 +27,19 @@ log_config: "/data/log.config"
rc_messages_per_second: 10000
rc_message_burst_count: 10000
rc_registration:
per_second: 10000
burst_count: 10000
per_second: 10000
burst_count: 10000
rc_login:
address:
per_second: 10000
burst_count: 10000
account:
per_second: 10000
burst_count: 10000
failed_attempts:
per_second: 10000
burst_count: 10000
address:
per_second: 10000
burst_count: 10000
account:
per_second: 10000
burst_count: 10000
failed_attempts:
per_second: 10000
burst_count: 10000
media_store_path: "/data/media_store"
uploads_path: "/data/uploads"
@@ -54,19 +54,19 @@ form_secret: "{{FORM_SECRET}}"
# Signing key must be here: it will be generated to this file
signing_key_path: "/data/localhost.signing.key"
email:
enable_notifs: false
smtp_host: "localhost"
smtp_port: 25
smtp_user: "exampleusername"
smtp_pass: "examplepassword"
require_transport_security: False
notif_from: "Your Friendly %(app)s homeserver <noreply@example.com>"
app_name: Matrix
notif_template_html: notif_mail.html
notif_template_text: notif_mail.txt
notif_for_new_users: True
client_base_url: "http://localhost/element"
enable_notifs: false
smtp_host: "localhost"
smtp_port: 25
smtp_user: "exampleusername"
smtp_pass: "examplepassword"
require_transport_security: False
notif_from: "Your Friendly %(app)s homeserver <noreply@example.com>"
app_name: Matrix
notif_template_html: notif_mail.html
notif_template_text: notif_mail.txt
notif_for_new_users: True
client_base_url: "http://localhost/element"
trusted_key_servers:
- server_name: "matrix.org"
- server_name: "matrix.org"
suppress_key_server_warning: true

View File

@@ -2,39 +2,39 @@ server_name: "localhost"
pid_file: /data/homeserver.pid
public_baseurl: "{{PUBLIC_BASEURL}}"
listeners:
- port: 8008
tls: false
bind_addresses: ['::']
type: http
x_forwarded: true
- port: 8008
tls: false
bind_addresses: ["::"]
type: http
x_forwarded: true
resources:
- names: [client, federation, consent]
compress: false
resources:
- names: [client, federation, consent]
compress: false
database:
name: "sqlite3"
args:
database: ":memory:"
name: "sqlite3"
args:
database: ":memory:"
log_config: "/data/log.config"
rc_messages_per_second: 10000
rc_message_burst_count: 10000
rc_registration:
per_second: 10000
burst_count: 10000
per_second: 10000
burst_count: 10000
rc_login:
address:
per_second: 10000
burst_count: 10000
account:
per_second: 10000
burst_count: 10000
failed_attempts:
per_second: 10000
burst_count: 10000
address:
per_second: 10000
burst_count: 10000
account:
per_second: 10000
burst_count: 10000
failed_attempts:
per_second: 10000
burst_count: 10000
media_store_path: "/data/media_store"
uploads_path: "/data/uploads"
@@ -47,38 +47,38 @@ macaroon_secret_key: "{{MACAROON_SECRET_KEY}}"
form_secret: "{{FORM_SECRET}}"
signing_key_path: "/data/localhost.signing.key"
email:
enable_notifs: false
smtp_host: "localhost"
smtp_port: 25
smtp_user: "exampleusername"
smtp_pass: "examplepassword"
require_transport_security: False
notif_from: "Your Friendly %(app)s homeserver <noreply@example.com>"
app_name: Matrix
notif_template_html: notif_mail.html
notif_template_text: notif_mail.txt
notif_for_new_users: True
client_base_url: "http://localhost/element"
enable_notifs: false
smtp_host: "localhost"
smtp_port: 25
smtp_user: "exampleusername"
smtp_pass: "examplepassword"
require_transport_security: False
notif_from: "Your Friendly %(app)s homeserver <noreply@example.com>"
app_name: Matrix
notif_template_html: notif_mail.html
notif_template_text: notif_mail.txt
notif_for_new_users: True
client_base_url: "http://localhost/element"
user_consent:
template_dir: /data/res/templates/privacy
version: 1.0
server_notice_content:
msgtype: m.text
body: >-
To continue using this homeserver you must review and agree to the
terms and conditions at %(consent_uri)s
send_server_notice_to_guests: True
block_events_error: >-
To continue using this homeserver you must review and agree to the
terms and conditions at %(consent_uri)s
require_at_registration: true
template_dir: /data/res/templates/privacy
version: 1.0
server_notice_content:
msgtype: m.text
body: >-
To continue using this homeserver you must review and agree to the
terms and conditions at %(consent_uri)s
send_server_notice_to_guests: True
block_events_error: >-
To continue using this homeserver you must review and agree to the
terms and conditions at %(consent_uri)s
require_at_registration: true
server_notices:
system_mxid_localpart: notices
system_mxid_display_name: "Server Notices"
system_mxid_avatar_url: "mxc://localhost:5005/oumMVlgDnLYFaPVkExemNVVZ"
room_name: "Server Notices"
system_mxid_localpart: notices
system_mxid_display_name: "Server Notices"
system_mxid_avatar_url: "mxc://localhost:5005/oumMVlgDnLYFaPVkExemNVVZ"
room_name: "Server Notices"
trusted_key_servers:
- server_name: "matrix.org"
- server_name: "matrix.org"
suppress_key_server_warning: true

View File

@@ -1,23 +1,19 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test Privacy policy</title>
</head>
<body>
{% if has_consented %}
<p>
Thank you, you've already accepted the license.
</p>
{% else %}
<p>
Please accept the license!
</p>
<form method="post" action="consent">
<input type="hidden" name="v" value="{{version}}"/>
<input type="hidden" name="u" value="{{user}}"/>
<input type="hidden" name="h" value="{{userhmac}}"/>
<input type="submit" value="Sure thing!"/>
</form>
{% endif %}
</body>
</html>
<head>
<title>Test Privacy policy</title>
</head>
<body>
{% if has_consented %}
<p>Thank you, you've already accepted the license.</p>
{% else %}
<p>Please accept the license!</p>
<form method="post" action="consent">
<input type="hidden" name="v" value="{{version}}" />
<input type="hidden" name="u" value="{{user}}" />
<input type="hidden" name="h" value="{{userhmac}}" />
<input type="submit" value="Sure thing!" />
</form>
{% endif %}
</body>
</html>

View File

@@ -1,9 +1,9 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test Privacy policy</title>
</head>
<body>
<p>Danke schon</p>
</body>
</html>
<head>
<title>Test Privacy policy</title>
</head>
<body>
<p>Danke schon</p>
</body>
</html>

View File

@@ -2,60 +2,60 @@ server_name: "localhost"
pid_file: /data/homeserver.pid
public_baseurl: "{{PUBLIC_BASEURL}}"
listeners:
- port: 8008
tls: false
bind_addresses: ['::']
type: http
x_forwarded: true
- port: 8008
tls: false
bind_addresses: ["::"]
type: http
x_forwarded: true
resources:
- names: [client]
compress: false
resources:
- names: [client]
compress: false
database:
name: "sqlite3"
args:
database: ":memory:"
name: "sqlite3"
args:
database: ":memory:"
log_config: "/data/log.config"
rc_messages_per_second: 10000
rc_message_burst_count: 10000
rc_registration:
per_second: 10000
burst_count: 10000
per_second: 10000
burst_count: 10000
rc_joins:
local:
per_second: 9999
burst_count: 9999
remote:
per_second: 9999
burst_count: 9999
local:
per_second: 9999
burst_count: 9999
remote:
per_second: 9999
burst_count: 9999
rc_joins_per_room:
per_second: 9999
burst_count: 9999
per_second: 9999
burst_count: 9999
rc_3pid_validation:
per_second: 1000
burst_count: 1000
per_second: 1000
burst_count: 1000
rc_invites:
per_room:
per_second: 1000
burst_count: 1000
per_user:
per_second: 1000
burst_count: 1000
per_room:
per_second: 1000
burst_count: 1000
per_user:
per_second: 1000
burst_count: 1000
rc_login:
address:
per_second: 10000
burst_count: 10000
account:
per_second: 10000
burst_count: 10000
failed_attempts:
per_second: 10000
burst_count: 10000
address:
per_second: 10000
burst_count: 10000
account:
per_second: 10000
burst_count: 10000
failed_attempts:
per_second: 10000
burst_count: 10000
media_store_path: "/data/media_store"
uploads_path: "/data/uploads"
@@ -69,8 +69,8 @@ form_secret: "{{FORM_SECRET}}"
signing_key_path: "/data/localhost.signing.key"
trusted_key_servers:
- server_name: "matrix.org"
- server_name: "matrix.org"
suppress_key_server_warning: true
ui_auth:
session_timeout: "300s"
session_timeout: "300s"

View File

@@ -17,7 +17,7 @@ limitations under the License.
import * as net from "net";
export async function getFreePort(): Promise<number> {
return new Promise<number>(resolve => {
return new Promise<number>((resolve) => {
const srv = net.createServer();
srv.listen(0, () => {
const port = (<net.AddressInfo>srv.address()).port;

View File

@@ -32,7 +32,7 @@ declare global {
}
Cypress.Commands.add("tweakConfig", (tweaks: Record<string, any>): Chainable<AUTWindow> => {
return cy.window().then(win => {
return cy.window().then((win) => {
// note: we can't *set* the object because the window version is effectively a pointer.
for (const [k, v] of Object.entries(tweaks)) {
// @ts-ignore - for some reason it's not picking up on global.d.ts types.
@@ -42,4 +42,4 @@ Cypress.Commands.add("tweakConfig", (tweaks: Record<string, any>): Chainable<AUT
});
// Needed to make this file a module
export { };
export {};

View File

@@ -24,10 +24,10 @@ import Chainable = Cypress.Chainable;
function terminalLog(violations: axe.Result[]): void {
cy.task(
'log',
`${violations.length} accessibility violation${
violations.length === 1 ? '' : 's'
} ${violations.length === 1 ? 'was' : 'were'} detected`,
"log",
`${violations.length} accessibility violation${violations.length === 1 ? "" : "s"} ${
violations.length === 1 ? "was" : "were"
} detected`,
);
// pluck specific keys to keep the table readable
@@ -38,24 +38,32 @@ function terminalLog(violations: axe.Result[]): void {
nodes: nodes.length,
}));
cy.task('table', violationData);
cy.task("table", violationData);
}
Cypress.Commands.overwrite("checkA11y", (
originalFn: Chainable["checkA11y"],
context?: string | Node | axe.ContextObject | undefined,
options: Options = {},
violationCallback?: ((violations: axe.Result[]) => void) | undefined,
skipFailures?: boolean,
): void => {
return originalFn(context, {
...options,
rules: {
// Disable contrast checking for now as we have too many issues with it
'color-contrast': {
enabled: false,
Cypress.Commands.overwrite(
"checkA11y",
(
originalFn: Chainable["checkA11y"],
context?: string | Node | axe.ContextObject | undefined,
options: Options = {},
violationCallback?: ((violations: axe.Result[]) => void) | undefined,
skipFailures?: boolean,
): void => {
return originalFn(
context,
{
...options,
rules: {
// Disable contrast checking for now as we have too many issues with it
"color-contrast": {
enabled: false,
},
...options.rules,
},
},
...options.rules,
},
}, violationCallback ?? terminalLog, skipFailures);
});
violationCallback ?? terminalLog,
skipFailures,
);
},
);

View File

@@ -77,9 +77,9 @@ Cypress.Commands.add("getBot", (synapse: SynapseInstance, opts: CreateBotOpts):
opts = Object.assign({}, defaultCreateBotOptions, opts);
const username = Cypress._.uniqueId("userId_");
const password = Cypress._.uniqueId("password_");
return cy.registerUser(synapse, username, password, opts.displayName).then(credentials => {
return cy.registerUser(synapse, username, password, opts.displayName).then((credentials) => {
cy.log(`Registered bot user ${username} with displayname ${opts.displayName}`);
return cy.window({ log: false }).then(win => {
return cy.window({ log: false }).then((win) => {
const cli = new win.matrixcs.MatrixClient({
baseUrl: synapse.baseUrl,
userId: credentials.userId,
@@ -103,12 +103,17 @@ Cypress.Commands.add("getBot", (synapse: SynapseInstance, opts: CreateBotOpts):
}
return cy.wrap(
cli.initCrypto()
cli
.initCrypto()
.then(() => cli.setGlobalErrorOnUnknownDevices(false))
.then(() => cli.startClient())
.then(() => cli.bootstrapCrossSigning({
authUploadDeviceSigningKeys: async func => { await func({}); },
}))
.then(() =>
cli.bootstrapCrossSigning({
authUploadDeviceSigningKeys: async (func) => {
await func({});
},
}),
)
.then(() => cli),
);
});
@@ -129,13 +134,15 @@ Cypress.Commands.add("botJoinRoomByName", (cli: MatrixClient, roomName: string):
return cy.wrap(Promise.reject(`Bot room join failed. Cannot find room '${roomName}'`));
});
Cypress.Commands.add("botSendMessage", (
cli: MatrixClient,
roomId: string,
message: string,
): Chainable<ISendEventResponse> => {
return cy.wrap(cli.sendMessage(roomId, {
msgtype: "m.text",
body: message,
}), { log: false });
});
Cypress.Commands.add(
"botSendMessage",
(cli: MatrixClient, roomId: string, message: string): Chainable<ISendEventResponse> => {
return cy.wrap(
cli.sendMessage(roomId, {
msgtype: "m.text",
body: message,
}),
{ log: false },
);
},
);

View File

@@ -66,7 +66,7 @@ declare global {
roomId: string,
threadId: string | null,
eventType: string,
content: IContent
content: IContent,
): Chainable<ISendEventResponse>;
/**
* @param {string} name
@@ -89,10 +89,7 @@ declare global {
* can be sent to XMLHttpRequest.send (typically a File). Under node.js,
* a a Buffer, String or ReadStream.
*/
uploadContent(
file: FileType,
opts?: UploadOpts,
): Chainable<Awaited<Upload["promise"]>>;
uploadContent(file: FileType, opts?: UploadOpts): Chainable<Awaited<Upload["promise"]>>;
/**
* Turn an MXC URL into an HTTP one. <strong>This method is experimental and
* may change.</strong>
@@ -133,23 +130,24 @@ declare global {
}
Cypress.Commands.add("getClient", (): Chainable<MatrixClient | undefined> => {
return cy.window({ log: false }).then(win => win.mxMatrixClientPeg.matrixClient);
return cy.window({ log: false }).then((win) => win.mxMatrixClientPeg.matrixClient);
});
Cypress.Commands.add("getDmRooms", (userId: string): Chainable<string[]> => {
return cy.getClient()
.then(cli => cli.getAccountData("m.direct")?.getContent<Record<string, string[]>>())
.then(dmRoomMap => dmRoomMap[userId] ?? []);
return cy
.getClient()
.then((cli) => cli.getAccountData("m.direct")?.getContent<Record<string, string[]>>())
.then((dmRoomMap) => dmRoomMap[userId] ?? []);
});
Cypress.Commands.add("createRoom", (options: ICreateRoomOpts): Chainable<string> => {
return cy.window({ log: false }).then(async win => {
return cy.window({ log: false }).then(async (win) => {
const cli = win.mxMatrixClientPeg.matrixClient;
const resp = await cli.createRoom(options);
const roomId = resp.room_id;
if (!cli.getRoom(roomId)) {
await new Promise<void>(resolve => {
await new Promise<void>((resolve) => {
const onRoom = (room: Room) => {
if (room.roomId === roomId) {
cli.off(win.matrixcs.ClientEvent.Room, onRoom);
@@ -168,7 +166,7 @@ Cypress.Commands.add("createSpace", (options: ICreateRoomOpts): Chainable<string
return cy.createRoom({
...options,
creation_content: {
"type": "m.space",
type: "m.space",
},
});
});
@@ -185,16 +183,14 @@ Cypress.Commands.add("setAccountData", (type: string, data: object): Chainable<{
});
});
Cypress.Commands.add("sendEvent", (
roomId: string,
threadId: string | null,
eventType: string,
content: IContent,
): Chainable<ISendEventResponse> => {
return cy.getClient().then(async (cli: MatrixClient) => {
return cli.sendEvent(roomId, threadId, eventType, content);
});
});
Cypress.Commands.add(
"sendEvent",
(roomId: string, threadId: string | null, eventType: string, content: IContent): Chainable<ISendEventResponse> => {
return cy.getClient().then(async (cli: MatrixClient) => {
return cli.sendEvent(roomId, threadId, eventType, content);
});
},
);
Cypress.Commands.add("setDisplayName", (name: string): Chainable<{}> => {
return cy.getClient().then(async (cli: MatrixClient) => {
@@ -215,13 +211,15 @@ Cypress.Commands.add("setAvatarUrl", (url: string): Chainable<{}> => {
});
Cypress.Commands.add("bootstrapCrossSigning", () => {
cy.window({ log: false }).then(win => {
cy.window({ log: false }).then((win) => {
win.mxMatrixClientPeg.matrixClient.bootstrapCrossSigning({
authUploadDeviceSigningKeys: async func => { await func({}); },
authUploadDeviceSigningKeys: async (func) => {
await func({});
},
});
});
});
Cypress.Commands.add("joinRoom", (roomIdOrAlias: string): Chainable<Room> => {
return cy.getClient().then(cli => cli.joinRoom(roomIdOrAlias));
return cy.getClient().then((cli) => cli.joinRoom(roomIdOrAlias));
});

View File

@@ -41,7 +41,7 @@ declare global {
}
Cypress.Commands.add("mockClipboard", () => {
cy.window({ log: false }).then(win => {
cy.window({ log: false }).then((win) => {
win.navigator.clipboard.writeText = (text) => {
copyText = text;
return Promise.resolve();
@@ -54,4 +54,4 @@ Cypress.Commands.add("getClipboardText", (): Chainable<string> => {
});
// Needed to make this file a module
export { };
export {};

View File

@@ -33,7 +33,7 @@ declare global {
}
Cypress.Commands.add("getComposer", (isRightPanel?: boolean): Chainable<JQuery> => {
const panelClass = isRightPanel ? '.mx_RightPanel' : '.mx_RoomView_body';
const panelClass = isRightPanel ? ".mx_RightPanel" : ".mx_RoomView_body";
return cy.get(`${panelClass} .mx_MessageComposer`);
});
@@ -41,8 +41,8 @@ Cypress.Commands.add("openMessageComposerOptions", (isRightPanel?: boolean): Cha
cy.getComposer(isRightPanel).within(() => {
cy.get('[aria-label="More options"]').click();
});
return cy.get('.mx_MessageComposer_Menu');
return cy.get(".mx_MessageComposer_Menu");
});
// Needed to make this file a module
export { };
export {};

View File

@@ -35,11 +35,15 @@ declare global {
// Inspired by https://www.cypress.io/blog/2020/02/12/working-with-iframes-in-cypress/
Cypress.Commands.add("accessIframe", (selector: string): Chainable<JQuery<HTMLElement>> => {
return cy.get(selector)
.its("0.contentDocument.body").should("not.be.empty")
// Cypress loses types in the mess of wrapping, so force cast
.then(cy.wrap) as Chainable<JQuery<HTMLElement>>;
return (
cy
.get(selector)
.its("0.contentDocument.body")
.should("not.be.empty")
// Cypress loses types in the mess of wrapping, so force cast
.then(cy.wrap) as Chainable<JQuery<HTMLElement>>
);
});
// Needed to make this file a module
export { };
export {};

View File

@@ -33,10 +33,13 @@ declare global {
}
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);
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 { };
export {};

View File

@@ -49,87 +49,97 @@ declare global {
* @param username login username
* @param password login password
*/
loginUser(
synapse: SynapseInstance,
username: string,
password: string,
): Chainable<UserCredentials>;
loginUser(synapse: SynapseInstance, username: string, password: string): Chainable<UserCredentials>;
}
}
}
// eslint-disable-next-line max-len
Cypress.Commands.add("loginUser", (synapse: SynapseInstance, username: string, password: string): Chainable<UserCredentials> => {
const url = `${synapse.baseUrl}/_matrix/client/r0/login`;
return cy.request<{
access_token: string;
user_id: string;
device_id: string;
home_server: string;
}>({
url,
method: "POST",
body: {
"type": "m.login.password",
"identifier": {
"type": "m.id.user",
"user": username,
Cypress.Commands.add(
"loginUser",
(synapse: SynapseInstance, username: string, password: string): Chainable<UserCredentials> => {
const url = `${synapse.baseUrl}/_matrix/client/r0/login`;
return cy
.request<{
access_token: string;
user_id: string;
device_id: string;
home_server: string;
}>({
url,
method: "POST",
body: {
type: "m.login.password",
identifier: {
type: "m.id.user",
user: username,
},
password: password,
},
"password": password,
},
}).then(response => ({
password,
username,
accessToken: response.body.access_token,
userId: response.body.user_id,
deviceId: response.body.device_id,
homeServer: response.body.home_server,
}));
});
})
.then((response) => ({
password,
username,
accessToken: response.body.access_token,
userId: response.body.user_id,
deviceId: response.body.device_id,
homeServer: response.body.home_server,
}));
},
);
// eslint-disable-next-line max-len
Cypress.Commands.add("initTestUser", (synapse: SynapseInstance, displayName: string, prelaunchFn?: () => void): Chainable<UserCredentials> => {
// XXX: work around Cypress not clearing IDB between tests
cy.window({ log: false }).then(win => {
win.indexedDB.databases()?.then(databases => {
databases.forEach(database => {
win.indexedDB.deleteDatabase(database.name);
Cypress.Commands.add(
"initTestUser",
(synapse: SynapseInstance, displayName: string, prelaunchFn?: () => void): Chainable<UserCredentials> => {
// XXX: work around Cypress not clearing IDB between tests
cy.window({ log: false }).then((win) => {
win.indexedDB.databases()?.then((databases) => {
databases.forEach((database) => {
win.indexedDB.deleteDatabase(database.name);
});
});
});
});
const username = Cypress._.uniqueId("userId_");
const password = Cypress._.uniqueId("password_");
return cy.registerUser(synapse, username, password, displayName).then(() => {
return cy.loginUser(synapse, username, password);
}).then(response => {
cy.log(`Registered test user ${username} with displayname ${displayName}`);
cy.window({ log: false }).then(win => {
// Seed the localStorage with the required credentials
win.localStorage.setItem("mx_hs_url", synapse.baseUrl);
win.localStorage.setItem("mx_user_id", response.userId);
win.localStorage.setItem("mx_access_token", response.accessToken);
win.localStorage.setItem("mx_device_id", response.deviceId);
win.localStorage.setItem("mx_is_guest", "false");
win.localStorage.setItem("mx_has_pickle_key", "false");
win.localStorage.setItem("mx_has_access_token", "true");
const username = Cypress._.uniqueId("userId_");
const password = Cypress._.uniqueId("password_");
return cy
.registerUser(synapse, username, password, displayName)
.then(() => {
return cy.loginUser(synapse, username, password);
})
.then((response) => {
cy.log(`Registered test user ${username} with displayname ${displayName}`);
cy.window({ log: false }).then((win) => {
// Seed the localStorage with the required credentials
win.localStorage.setItem("mx_hs_url", synapse.baseUrl);
win.localStorage.setItem("mx_user_id", response.userId);
win.localStorage.setItem("mx_access_token", response.accessToken);
win.localStorage.setItem("mx_device_id", response.deviceId);
win.localStorage.setItem("mx_is_guest", "false");
win.localStorage.setItem("mx_has_pickle_key", "false");
win.localStorage.setItem("mx_has_access_token", "true");
// Ensure the language is set to a consistent value
win.localStorage.setItem("mx_local_settings", '{"language":"en"}');
});
// Ensure the language is set to a consistent value
win.localStorage.setItem("mx_local_settings", '{"language":"en"}');
});
prelaunchFn?.();
prelaunchFn?.();
return cy.visit("/").then(() => {
// wait for the app to load
return cy.get(".mx_MatrixChat", { timeout: 30000 });
}).then(() => ({
password,
username,
accessToken: response.accessToken,
userId: response.userId,
deviceId: response.deviceId,
homeServer: response.homeServer,
}));
});
});
return cy
.visit("/")
.then(() => {
// wait for the app to load
return cy.get(".mx_MatrixChat", { timeout: 30000 });
})
.then(() => ({
password,
username,
accessToken: response.accessToken,
userId: response.userId,
deviceId: response.deviceId,
homeServer: response.homeServer,
}));
});
},
);

View File

@@ -35,27 +35,35 @@ declare global {
Cypress.Commands.add("goOffline", (): void => {
cy.log("Going offline");
cy.window({ log: false }).then(win => {
cy.intercept("**/_matrix/**", {
headers: {
"Authorization": "Bearer " + win.mxMatrixClientPeg.matrixClient.getAccessToken(),
cy.window({ log: false }).then((win) => {
cy.intercept(
"**/_matrix/**",
{
headers: {
Authorization: "Bearer " + win.mxMatrixClientPeg.matrixClient.getAccessToken(),
},
},
}, req => {
req.destroy();
});
(req) => {
req.destroy();
},
);
});
});
Cypress.Commands.add("goOnline", (): void => {
cy.log("Going online");
cy.window({ log: false }).then(win => {
cy.intercept("**/_matrix/**", {
headers: {
"Authorization": "Bearer " + win.mxMatrixClientPeg.matrixClient.getAccessToken(),
cy.window({ log: false }).then((win) => {
cy.intercept(
"**/_matrix/**",
{
headers: {
Authorization: "Bearer " + win.mxMatrixClientPeg.matrixClient.getAccessToken(),
},
},
}, req => {
req.continue();
});
(req) => {
req.continue();
},
);
win.dispatchEvent(new Event("online"));
});
});
@@ -85,4 +93,4 @@ Cypress.Commands.add("stubDefaultServer", (): void => {
});
// Needed to make this file a module
export { };
export {};

View File

@@ -15,7 +15,7 @@ limitations under the License.
*/
/// <reference types="cypress" />
import { SnapshotOptions as PercySnapshotOptions } from '@percy/core';
import { SnapshotOptions as PercySnapshotOptions } from "@percy/core";
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
@@ -39,16 +39,16 @@ declare global {
Cypress.Commands.add("percySnapshotElement", { prevSubject: "element" }, (subject, name, options) => {
cy.percySnapshot(name, {
domTransformation: documentClone => scope(documentClone, subject.selector),
domTransformation: (documentClone) => scope(documentClone, subject.selector),
...options,
});
});
function scope(documentClone: Document, selector: string): Document {
const element = documentClone.querySelector(selector);
documentClone.querySelector('body').innerHTML = element.outerHTML;
documentClone.querySelector("body").innerHTML = element.outerHTML;
return documentClone;
}
export { };
export {};

View File

@@ -18,7 +18,7 @@ limitations under the License.
import Chainable = Cypress.Chainable;
import AUTWindow = Cypress.AUTWindow;
import { ProxyInstance } from '../plugins/sliding-sync';
import { ProxyInstance } from "../plugins/sliding-sync";
import { SynapseInstance } from "../plugins/synapsedocker";
declare global {
@@ -49,7 +49,7 @@ function stopProxy(proxy?: ProxyInstance): Chainable<AUTWindow> {
if (!proxy) return;
// Navigate away from app to stop the background network requests which will race with Synapse shutting down
return cy.window({ log: false }).then((win) => {
win.location.href = 'about:blank';
win.location.href = "about:blank";
cy.task("proxyStop", proxy);
});
}

View File

@@ -102,26 +102,27 @@ declare global {
}
Cypress.Commands.add("getSettingsStore", (): Chainable<typeof SettingsStore> => {
return cy.window({ log: false }).then(win => win.mxSettingsStore);
return cy.window({ log: false }).then((win) => win.mxSettingsStore);
});
Cypress.Commands.add("setSettingValue", (
name: string,
roomId: string,
level: SettingLevel,
value: any,
): Chainable<void> => {
return cy.getSettingsStore().then((store: typeof SettingsStore) => {
return cy.wrap(store.setValue(name, roomId, level, value));
});
});
Cypress.Commands.add(
"setSettingValue",
(name: string, roomId: string, level: SettingLevel, value: any): Chainable<void> => {
return cy.getSettingsStore().then((store: typeof SettingsStore) => {
return cy.wrap(store.setValue(name, roomId, level, value));
});
},
);
// eslint-disable-next-line max-len
Cypress.Commands.add("getSettingValue", <T = any>(name: string, roomId?: string, excludeDefault?: boolean): Chainable<T> => {
return cy.getSettingsStore().then((store: typeof SettingsStore) => {
return store.getValue(name, roomId, excludeDefault);
});
});
Cypress.Commands.add(
"getSettingValue",
<T = any>(name: string, roomId?: string, excludeDefault?: boolean): Chainable<T> => {
return cy.getSettingsStore().then((store: typeof SettingsStore) => {
return store.getValue(name, roomId, excludeDefault);
});
},
);
Cypress.Commands.add("openUserMenu", (): Chainable<JQuery<HTMLElement>> => {
cy.get('[aria-label="User menu"]').click();
@@ -162,16 +163,22 @@ Cypress.Commands.add("closeDialog", (): Chainable<JQuery<HTMLElement>> => {
});
Cypress.Commands.add("joinBeta", (name: string): Chainable<JQuery<HTMLElement>> => {
return cy.contains(".mx_BetaCard_title", name).closest(".mx_BetaCard").within(() => {
return cy.get(".mx_BetaCard_buttons").contains("Join the beta").click();
});
return cy
.contains(".mx_BetaCard_title", name)
.closest(".mx_BetaCard")
.within(() => {
return cy.get(".mx_BetaCard_buttons").contains("Join the beta").click();
});
});
Cypress.Commands.add("leaveBeta", (name: string): Chainable<JQuery<HTMLElement>> => {
return cy.contains(".mx_BetaCard_title", name).closest(".mx_BetaCard").within(() => {
return cy.get(".mx_BetaCard_buttons").contains("Leave the beta").click();
});
return cy
.contains(".mx_BetaCard_title", name)
.closest(".mx_BetaCard")
.within(() => {
return cy.get(".mx_BetaCard_buttons").contains("Leave the beta").click();
});
});
// Needed to make this file a module
export { };
export {};

View File

@@ -16,7 +16,7 @@ limitations under the License.
/// <reference types="cypress" />
import * as crypto from 'crypto';
import * as crypto from "crypto";
import Chainable = Cypress.Chainable;
import AUTWindow = Cypress.AUTWindow;
@@ -64,7 +64,7 @@ function stopSynapse(synapse?: SynapseInstance): Chainable<AUTWindow> {
if (!synapse) return;
// Navigate away from app to stop the background network requests which will race with Synapse shutting down
return cy.window({ log: false }).then((win) => {
win.location.href = 'about:blank';
win.location.href = "about:blank";
cy.task("synapseStop", synapse.synapseId);
});
}
@@ -83,38 +83,42 @@ function registerUser(
displayName?: string,
): Chainable<Credentials> {
const url = `${synapse.baseUrl}/_synapse/admin/v1/register`;
return cy.then(() => {
// get a nonce
return cy.request<{ nonce: string }>({ url });
}).then(response => {
const { nonce } = response.body;
const mac = crypto.createHmac('sha1', synapse.registrationSecret).update(
`${nonce}\0${username}\0${password}\0notadmin`,
).digest('hex');
return cy
.then(() => {
// get a nonce
return cy.request<{ nonce: string }>({ url });
})
.then((response) => {
const { nonce } = response.body;
const mac = crypto
.createHmac("sha1", synapse.registrationSecret)
.update(`${nonce}\0${username}\0${password}\0notadmin`)
.digest("hex");
return cy.request<{
access_token: string;
user_id: string;
home_server: string;
device_id: string;
}>({
url,
method: "POST",
body: {
nonce,
username,
password,
mac,
admin: false,
displayname: displayName,
},
});
}).then(response => ({
homeServer: response.body.home_server,
accessToken: response.body.access_token,
userId: response.body.user_id,
deviceId: response.body.device_id,
}));
return cy.request<{
access_token: string;
user_id: string;
home_server: string;
device_id: string;
}>({
url,
method: "POST",
body: {
nonce,
username,
password,
mac,
admin: false,
displayname: displayName,
},
});
})
.then((response) => ({
homeServer: response.body.home_server,
accessToken: response.body.access_token,
userId: response.body.user_id,
deviceId: response.body.device_id,
}));
}
Cypress.Commands.add("startSynapse", startSynapse);

View File

@@ -38,17 +38,19 @@ export interface Message {
}
Cypress.Commands.add("scrollToTop", (): void => {
cy.get(".mx_RoomView_timeline .mx_ScrollPanel").scrollTo("top", { duration: 100 }).then(ref => {
if (ref.scrollTop() > 0) {
return cy.scrollToTop();
}
});
cy.get(".mx_RoomView_timeline .mx_ScrollPanel")
.scrollTo("top", { duration: 100 })
.then((ref) => {
if (ref.scrollTop() > 0) {
return cy.scrollToTop();
}
});
});
Cypress.Commands.add("findEventTile", (sender: string, body: string): Chainable<JQuery> => {
// We can't just use a bunch of `.contains` here due to continuations meaning that the events don't
// have their own rendered sender displayname so we have to walk the list to keep track of the sender.
return cy.get(".mx_RoomView_MessageList .mx_EventTile").then(refs => {
return cy.get(".mx_RoomView_MessageList .mx_EventTile").then((refs) => {
let latestSender: string;
for (let i = 0; i < refs.length; i++) {
const ref = refs.eq(i);
@@ -65,4 +67,4 @@ Cypress.Commands.add("findEventTile", (sender: string, body: string): Chainable<
});
// Needed to make this file a module
export { };
export {};

View File

@@ -29,7 +29,7 @@ declare global {
interface cy {
all<T extends Cypress.Chainable[] | []>(
commands: T
commands: T,
): Cypress.Chainable<{ [P in keyof T]: ChainableValue<T[P]> }>;
queue: any;
}
@@ -59,16 +59,16 @@ cy.all = function all(commands): Cypress.Chainable {
return cy.wrap(
// @see https://lodash.com/docs/4.17.15#lodash
Cypress._(commands)
.map(cmd => {
.map((cmd) => {
return cmd[chainStart]
? cmd[chainStart].attributes
: Cypress._.find(cy.queue.get(), {
attributes: { chainerId: cmd.chainerId },
}).attributes;
attributes: { chainerId: cmd.chainerId },
}).attributes;
})
.concat(stopCommand.attributes)
.slice(1)
.map(cmd => {
.map((cmd) => {
return cmd.prev.get("subject");
})
.value(),
@@ -79,4 +79,4 @@ cy.all = function all(commands): Cypress.Chainable {
};
// Needed to make this file a module
export { };
export {};

View File

@@ -70,4 +70,4 @@ Cypress.Commands.add("viewSpaceHomeByName", (name: string): Chainable<JQuery<HTM
});
// Needed to make this file a module
export { };
export {};

View File

@@ -49,4 +49,4 @@ Cypress.Commands.add("serveHtmlFile", serveHtmlFile);
Cypress.Commands.add("stopWebServers", stopWebServers);
// Needed to make this file a module
export { };
export {};

View File

@@ -2,22 +2,12 @@
"compilerOptions": {
"target": "es2016",
"jsx": "react",
"lib": [
"es2020",
"dom",
"dom.iterable"
],
"types": [
"cypress",
"cypress-axe",
"@percy/cypress"
],
"lib": ["es2020", "dom", "dom.iterable"],
"types": ["cypress", "cypress-axe", "@percy/cypress"],
"resolveJsonModule": true,
"esModuleInterop": true,
"moduleResolution": "node",
"module": "commonjs"
},
"include": [
"**/*.ts"
]
"include": ["**/*.ts"]
}