Rename sliding-sync.ts to sliding-sync.spec.ts (#10723)
Follow up to #10709. Will hopefully make the CI on matrix-org/sliding-sync greener. Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
96bcfc58aa
commit
8eac9f821a
1 changed files with 0 additions and 0 deletions
502
cypress/e2e/sliding-sync/sliding-sync.spec.ts
Normal file
502
cypress/e2e/sliding-sync/sliding-sync.spec.ts
Normal file
|
@ -0,0 +1,502 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
import _ from "lodash";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { Interception } from "cypress/types/net-stubbing";
|
||||
|
||||
import { HomeserverInstance } from "../../plugins/utils/homeserver";
|
||||
import { ProxyInstance } from "../../plugins/sliding-sync";
|
||||
|
||||
describe("Sliding Sync", () => {
|
||||
beforeEach(() => {
|
||||
cy.startHomeserver("default")
|
||||
.as("homeserver")
|
||||
.then((homeserver) => {
|
||||
cy.startProxy(homeserver).as("proxy");
|
||||
});
|
||||
|
||||
cy.all([cy.get<HomeserverInstance>("@homeserver"), cy.get<ProxyInstance>("@proxy")]).then(
|
||||
([homeserver, proxy]) => {
|
||||
cy.enableLabsFeature("feature_sliding_sync");
|
||||
|
||||
cy.intercept("/config.json?cachebuster=*", (req) => {
|
||||
return req.continue((res) => {
|
||||
res.send(200, {
|
||||
...res.body,
|
||||
setting_defaults: {
|
||||
feature_sliding_sync_proxy_url: `http://localhost:${proxy.port}`,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
cy.initTestUser(homeserver, "Sloth").then(() => {
|
||||
return cy.window({ log: false }).then(() => {
|
||||
cy.createRoom({ name: "Test Room" }).as("roomId");
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.get<HomeserverInstance>("@homeserver").then(cy.stopHomeserver);
|
||||
cy.get<ProxyInstance>("@proxy").then(cy.stopProxy);
|
||||
});
|
||||
|
||||
// assert order
|
||||
const checkOrder = (wantOrder: string[]) => {
|
||||
cy.findByRole("group", { name: "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
|
||||
cy.get<string>(alias).then((roomId) => {
|
||||
return cy.sendEvent(roomId, null, "m.room.message", {
|
||||
body: "Hello world",
|
||||
msgtype: "m.text",
|
||||
});
|
||||
});
|
||||
};
|
||||
const createAndJoinBob = () => {
|
||||
// create a Bob user
|
||||
cy.get<HomeserverInstance>("@homeserver").then((homeserver) => {
|
||||
return cy
|
||||
.getBot(homeserver, {
|
||||
displayName: "Bob",
|
||||
})
|
||||
.as("bob");
|
||||
});
|
||||
|
||||
// invite Bob to Test Room and accept then send a message.
|
||||
cy.all([cy.get<string>("@roomId"), cy.get<MatrixClient>("@bob")]).then(([roomId, bob]) => {
|
||||
return cy.inviteUser(roomId, bob.getUserId()).then(() => {
|
||||
return bob.joinRoom(roomId);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
it("should render the Rooms list in reverse chronological order by default and allowing sorting A-Z", () => {
|
||||
// create rooms and check room names are correct
|
||||
cy.createRoom({ name: "Apple" }).then(() => cy.findByRole("treeitem", { name: "Apple" }));
|
||||
cy.createRoom({ name: "Pineapple" }).then(() => cy.findByRole("treeitem", { name: "Pineapple" }));
|
||||
cy.createRoom({ name: "Orange" }).then(() => cy.findByRole("treeitem", { name: "Orange" }));
|
||||
|
||||
cy.get(".mx_RoomSublist_tiles").within(() => {
|
||||
cy.findAllByRole("treeitem").should("have.length", 4); // due to the Test Room in beforeEach
|
||||
});
|
||||
|
||||
checkOrder(["Orange", "Pineapple", "Apple", "Test Room"]);
|
||||
|
||||
cy.findByRole("group", { name: "Rooms" }).within(() => {
|
||||
cy.get(".mx_RoomSublist_headerContainer")
|
||||
.realHover()
|
||||
.findByRole("button", { name: "List options" })
|
||||
.click();
|
||||
});
|
||||
|
||||
// force click as the radio button's size is zero
|
||||
cy.findByRole("menuitemradio", { name: "A-Z" }).click({ force: true });
|
||||
|
||||
// Assert that the radio button is checked
|
||||
cy.get(".mx_StyledRadioButton_checked").within(() => {
|
||||
cy.findByText("A-Z").should("exist");
|
||||
});
|
||||
|
||||
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.findByRole("treeitem", { name: "Apple" }));
|
||||
cy.createRoom({ name: "Pineapple" })
|
||||
.as("roomP")
|
||||
.then(() => cy.findByRole("treeitem", { name: "Pineapple" }));
|
||||
cy.createRoom({ name: "Orange" })
|
||||
.as("roomO")
|
||||
.then(() => cy.findByRole("treeitem", { name: "Orange" }));
|
||||
|
||||
// Select the Test Room
|
||||
cy.findByRole("treeitem", { name: "Test Room" }).click();
|
||||
|
||||
checkOrder(["Orange", "Pineapple", "Apple", "Test Room"]);
|
||||
bumpRoom("@roomA");
|
||||
checkOrder(["Apple", "Orange", "Pineapple", "Test Room"]);
|
||||
bumpRoom("@roomO");
|
||||
checkOrder(["Orange", "Apple", "Pineapple", "Test Room"]);
|
||||
bumpRoom("@roomO");
|
||||
checkOrder(["Orange", "Apple", "Pineapple", "Test Room"]);
|
||||
bumpRoom("@roomP");
|
||||
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.findByRole("treeitem", { name: "Apple" }));
|
||||
cy.createRoom({ name: "Pineapple" })
|
||||
.as("roomP")
|
||||
.then(() => cy.findByRole("treeitem", { name: "Pineapple" }));
|
||||
cy.createRoom({ name: "Orange" })
|
||||
.as("roomO")
|
||||
.then(() => cy.findByRole("treeitem", { name: "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
|
||||
// be Apple, Orange Pineapple - only when you click on a different room do things reshuffle.
|
||||
|
||||
// Select the Pineapple room
|
||||
cy.findByRole("treeitem", { name: "Pineapple" }).click();
|
||||
checkOrder(["Orange", "Pineapple", "Apple", "Test Room"]);
|
||||
|
||||
// Move Apple
|
||||
bumpRoom("@roomA");
|
||||
checkOrder(["Apple", "Pineapple", "Orange", "Test Room"]);
|
||||
|
||||
// Select the Test Room
|
||||
cy.findByRole("treeitem", { name: "Test Room" }).click();
|
||||
|
||||
// the rooms reshuffle to match reality
|
||||
checkOrder(["Apple", "Orange", "Pineapple", "Test Room"]);
|
||||
});
|
||||
|
||||
it("should show the right unread notifications", () => {
|
||||
createAndJoinBob();
|
||||
|
||||
// send a message in the test room: unread notif count shoould increment
|
||||
cy.all([cy.get<string>("@roomId"), cy.get<MatrixClient>("@bob")]).then(([roomId, bob]) => {
|
||||
return bob.sendTextMessage(roomId, "Hello World");
|
||||
});
|
||||
|
||||
// check that there is an unread notification (grey) as 1
|
||||
cy.findByRole("treeitem", { name: "Test Room 1 unread message." }).contains(".mx_NotificationBadge_count", "1");
|
||||
cy.get(".mx_NotificationBadge").should("not.have.class", "mx_NotificationBadge_highlighted");
|
||||
|
||||
// send an @mention: highlight count (red) should be 2.
|
||||
cy.all([cy.get<string>("@roomId"), cy.get<MatrixClient>("@bob")]).then(([roomId, bob]) => {
|
||||
return bob.sendTextMessage(roomId, "Hello Sloth");
|
||||
});
|
||||
cy.findByRole("treeitem", { name: "Test Room 2 unread messages including mentions." }).contains(
|
||||
".mx_NotificationBadge_count",
|
||||
"2",
|
||||
);
|
||||
cy.get(".mx_NotificationBadge").should("have.class", "mx_NotificationBadge_highlighted");
|
||||
|
||||
// click on the room, the notif counts should disappear
|
||||
cy.findByRole("treeitem", { name: "Test Room 2 unread messages including mentions." }).click();
|
||||
cy.findByRole("treeitem", { name: "Test Room" }).should("not.have.class", "mx_NotificationBadge_count");
|
||||
});
|
||||
|
||||
it("should not show unread indicators", () => {
|
||||
// TODO: for now. Later we should.
|
||||
createAndJoinBob();
|
||||
|
||||
// disable notifs in this room (TODO: CS API call?)
|
||||
cy.findByRole("treeitem", { name: "Test Room" })
|
||||
.realHover()
|
||||
.findByRole("button", { name: "Notification options" })
|
||||
.click();
|
||||
cy.findByRole("menuitemradio", { name: "Mute room" }).click();
|
||||
|
||||
// create a new room so we know when the message has been received as it'll re-shuffle the room list
|
||||
cy.createRoom({
|
||||
name: "Dummy",
|
||||
});
|
||||
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"]);
|
||||
|
||||
cy.findByRole("treeitem", { name: "Test Room" }).get(".mx_NotificationBadge").should("not.exist");
|
||||
});
|
||||
|
||||
it("should update user settings promptly", () => {
|
||||
cy.openUserSettings("Preferences");
|
||||
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", () => {
|
||||
createAndJoinBob();
|
||||
|
||||
let clientUserId;
|
||||
cy.getClient().then((cli) => {
|
||||
clientUserId = cli.getUserId();
|
||||
});
|
||||
|
||||
// invite Sloth into 3 rooms:
|
||||
// - 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: "Room to Join" }),
|
||||
bob.createRoom({ name: "Room to Reject" }),
|
||||
bob.createRoom({ name: "Room to 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),
|
||||
]);
|
||||
});
|
||||
|
||||
cy.findByRole("group", { name: "Invites" }).within(() => {
|
||||
// Exclude headerText
|
||||
cy.get(".mx_RoomSublist_tiles").within(() => {
|
||||
// Wait for them all to be on the UI
|
||||
cy.findAllByRole("treeitem").should("have.length", 3);
|
||||
});
|
||||
});
|
||||
|
||||
// Select the room to join
|
||||
cy.findByRole("treeitem", { name: "Room to Join" }).click();
|
||||
|
||||
cy.get(".mx_RoomView").within(() => {
|
||||
// Accept the invite
|
||||
cy.findByRole("button", { name: "Accept" }).click();
|
||||
});
|
||||
|
||||
checkOrder(["Room to Join", "Test Room"]);
|
||||
|
||||
// Select the room to reject
|
||||
cy.findByRole("treeitem", { name: "Room to Reject" }).click();
|
||||
|
||||
cy.get(".mx_RoomView").within(() => {
|
||||
// Reject the invite
|
||||
cy.findByRole("button", { name: "Reject" }).click();
|
||||
});
|
||||
|
||||
cy.findByRole("group", { name: "Invites" }).within(() => {
|
||||
// Exclude headerText
|
||||
cy.get(".mx_RoomSublist_tiles").within(() => {
|
||||
// Wait for the rejected room to disappear
|
||||
cy.findAllByRole("treeitem").should("have.length", 2);
|
||||
});
|
||||
});
|
||||
|
||||
// check the lists are correct
|
||||
checkOrder(["Room to Join", "Test Room"]);
|
||||
|
||||
cy.findByRole("group", { name: "Invites" })
|
||||
.find(".mx_RoomTile_title")
|
||||
.should((elements) => {
|
||||
expect(
|
||||
_.map(elements, (e) => {
|
||||
return e.textContent;
|
||||
}),
|
||||
"rooms are sorted",
|
||||
).to.deep.equal(["Room to Rescind"]);
|
||||
});
|
||||
|
||||
// now rescind the invite
|
||||
cy.get<MatrixClient>("@bob").then((bob) => {
|
||||
return bob.kick(roomRescind, clientUserId);
|
||||
});
|
||||
|
||||
cy.findByRole("group", { name: "Rooms" }).within(() => {
|
||||
// Exclude headerText
|
||||
cy.get(".mx_RoomSublist_tiles").within(() => {
|
||||
// Wait for the rescind to take effect and check the joined list once more
|
||||
cy.findAllByRole("treeitem").should("have.length", 2);
|
||||
});
|
||||
});
|
||||
|
||||
checkOrder(["Room to 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 }));
|
||||
});
|
||||
|
||||
cy.findByRole("group", { name: "Favourites" }).findByText("Favourite DM").should("exist");
|
||||
cy.findByRole("group", { name: "People" }).findByText("Favourite DM").should("not.exist");
|
||||
});
|
||||
|
||||
// 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.findByRole("treeitem", { name: "Other Room" }));
|
||||
cy.get<string>("@roomId").then((roomId) => {
|
||||
return cy.sendEvent(roomId, null, "m.room.message", {
|
||||
body: "Hello world",
|
||||
msgtype: "m.text",
|
||||
});
|
||||
});
|
||||
// select the room
|
||||
cy.findByRole("treeitem", { name: "Test Room" }).click();
|
||||
cy.get(".mx_ReplyPreview").should("not.exist");
|
||||
// click reply-to on the Hello World message
|
||||
cy.get(".mx_EventTile_last")
|
||||
.within(() => {
|
||||
cy.findByText("Hello world", { timeout: 1000 });
|
||||
})
|
||||
.realHover()
|
||||
.findByRole("button", { name: "Reply" })
|
||||
.click();
|
||||
// check it's visible
|
||||
cy.get(".mx_ReplyPreview").should("exist");
|
||||
// now click Other Room
|
||||
cy.findByRole("treeitem", { name: "Other Room" }).click();
|
||||
// ensure the reply-to disappears
|
||||
cy.get(".mx_ReplyPreview").should("not.exist");
|
||||
// click back
|
||||
cy.findByRole("treeitem", { name: "Test Room" }).click();
|
||||
// ensure the reply-to reappears
|
||||
cy.get(".mx_ReplyPreview").should("exist");
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/vector-im/element-web/issues/21462
|
||||
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",
|
||||
msgtype: "m.text",
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
cy.sendEvent(roomId, null, "m.room.message", {
|
||||
body: "Reply to me",
|
||||
msgtype: "m.text",
|
||||
});
|
||||
});
|
||||
});
|
||||
// select the room
|
||||
cy.findByRole("treeitem", { name: "Test Room" }).click();
|
||||
cy.get(".mx_ReplyPreview").should("not.exist");
|
||||
// click reply-to on the Reply to me message
|
||||
cy.get(".mx_EventTile")
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.findByText("Reply to me");
|
||||
})
|
||||
.realHover()
|
||||
.findByRole("button", { name: "Reply" })
|
||||
.click();
|
||||
// check it's visible
|
||||
cy.get(".mx_ReplyPreview").should("exist");
|
||||
// now click on the permalink for Permalink me
|
||||
cy.contains(".mx_EventTile", "Permalink me").find("a").click({ force: true });
|
||||
// make sure it is now selected with the little green |
|
||||
cy.contains(".mx_EventTile_selected", "Permalink me").should("exist");
|
||||
// ensure the reply-to does not disappear
|
||||
cy.get(".mx_ReplyPreview").should("exist");
|
||||
});
|
||||
|
||||
it("should send unsubscribe_rooms for every room switch", () => {
|
||||
let roomAId: string;
|
||||
let roomPId: string;
|
||||
// create rooms and check room names are correct
|
||||
cy.createRoom({ name: "Apple" })
|
||||
.as("roomA")
|
||||
.then((roomId) => (roomAId = roomId))
|
||||
.then(() => cy.findByRole("treeitem", { name: "Apple" }));
|
||||
|
||||
cy.createRoom({ name: "Pineapple" })
|
||||
.as("roomP")
|
||||
.then((roomId) => (roomPId = roomId))
|
||||
.then(() => cy.findByRole("treeitem", { name: "Pineapple" }));
|
||||
cy.createRoom({ name: "Orange" })
|
||||
.as("roomO")
|
||||
.then(() => cy.findByRole("treeitem", { name: "Orange" }));
|
||||
|
||||
// Intercept all calls to /sync
|
||||
cy.intercept({ method: "POST", url: "**/sync*" }).as("syncRequest");
|
||||
|
||||
const assertUnsubExists = (interception: Interception, subRoomId: string, unsubRoomId: string) => {
|
||||
const body = interception.request.body;
|
||||
// There may be a request without a txn_id, ignore it, as there won't be any subscription changes
|
||||
if (body.txn_id === undefined) {
|
||||
return;
|
||||
}
|
||||
expect(body.unsubscribe_rooms).eql([unsubRoomId]);
|
||||
expect(body.room_subscriptions).to.not.have.property(unsubRoomId);
|
||||
expect(body.room_subscriptions).to.have.property(subRoomId);
|
||||
};
|
||||
|
||||
// Select the Test Room
|
||||
cy.findByRole("treeitem", { name: "Apple" }).click();
|
||||
|
||||
// and wait for cypress to get the result as alias
|
||||
cy.wait("@syncRequest").then((interception) => {
|
||||
// This is the first switch, so no unsubscriptions yet.
|
||||
assert.isObject(interception.request.body.room_subscriptions, "room_subscriptions is object");
|
||||
});
|
||||
|
||||
// Switch to another room
|
||||
cy.findByRole("treeitem", { name: "Pineapple" }).click();
|
||||
cy.wait("@syncRequest").then((interception) => assertUnsubExists(interception, roomPId, roomAId));
|
||||
|
||||
// And switch to even another room
|
||||
cy.findByRole("treeitem", { name: "Apple" }).click();
|
||||
cy.wait("@syncRequest").then((interception) => assertUnsubExists(interception, roomPId, roomAId));
|
||||
|
||||
// TODO: Add tests for encrypted rooms
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue