Move threads e2e tests over to cypress (#8501)
* Add non-consent (default) Synapse template * Add consent test * Add create room test * Stash work * Initial threads tests * fix * Delete old threads e2e tests, plan new ones * Fix typed s'more * Try something else * specify d.ts * Fix types once and for all? * Fix the consent tests * Iterate threads test harness * Fix dispatcher types * Iterate threads test * fix typing * Alternative import attempt * let it break let it break let it break * Tweak types * Stash * delint and update docs * null-guard scrollIntoView * Iterate threads test * Apply suggestions from code review
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
import { SinonStub } from "cypress/types/sinon";
|
||||
|
||||
import { SynapseInstance } from "../../plugins/synapsedocker";
|
||||
|
||||
describe("Consent", () => {
|
||||
let synapse: SynapseInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.startSynapse("consent").then(data => {
|
||||
synapse = data;
|
||||
|
||||
cy.initTestUser(synapse, "Bob");
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopSynapse(synapse);
|
||||
});
|
||||
|
||||
it("should prompt the user to consent to terms when server deems it necessary", () => {
|
||||
// Attempt to create a room using the js-sdk which should return an error with `M_CONSENT_NOT_GIVEN`
|
||||
cy.window().then(win => {
|
||||
win.mxMatrixClientPeg.matrixClient.createRoom({}).catch(() => {});
|
||||
|
||||
// Stub `window.open` - clicking the primary button below will call it
|
||||
cy.stub(win, "open").as("windowOpen").returns({});
|
||||
});
|
||||
|
||||
// Accept terms & conditions
|
||||
cy.get(".mx_QuestionDialog").within(() => {
|
||||
cy.get("#mx_BaseDialog_title").contains("Terms and Conditions");
|
||||
cy.get(".mx_Dialog_primary").click();
|
||||
});
|
||||
|
||||
cy.get<SinonStub>("@windowOpen").then(stub => {
|
||||
const url = stub.getCall(0).args[0];
|
||||
|
||||
// Go to Synapse's consent page and accept it
|
||||
cy.origin(synapse.baseUrl, { args: { url } }, ({ url }) => {
|
||||
cy.visit(url);
|
||||
|
||||
cy.get('[type="submit"]').click();
|
||||
cy.get("p").contains("Danke schon");
|
||||
});
|
||||
});
|
||||
|
||||
// go back to the app
|
||||
cy.visit("/");
|
||||
// wait for the app to re-load
|
||||
cy.get(".mx_MatrixChat", { timeout: 15000 });
|
||||
|
||||
// attempt to perform the same action again and expect it to not fail
|
||||
cy.createRoom({});
|
||||
});
|
||||
});
|
||||
@@ -19,12 +19,12 @@ limitations under the License.
|
||||
import { SynapseInstance } from "../../plugins/synapsedocker";
|
||||
import type { UserCredentials } from "../../support/login";
|
||||
|
||||
describe("UserMenu", () => {
|
||||
describe("User Menu", () => {
|
||||
let synapse: SynapseInstance;
|
||||
let user: UserCredentials;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.startSynapse("consent").then(data => {
|
||||
cy.startSynapse("default").then(data => {
|
||||
synapse = data;
|
||||
|
||||
cy.initTestUser(synapse, "Jeff").then(credentials => {
|
||||
@@ -38,8 +38,10 @@ describe("UserMenu", () => {
|
||||
});
|
||||
|
||||
it("should contain our name & userId", () => {
|
||||
cy.get('[aria-label="User menu"]', { timeout: 15000 }).click();
|
||||
cy.get(".mx_UserMenu_contextMenu_displayName").should("contain", "Jeff");
|
||||
cy.get(".mx_UserMenu_contextMenu_userId").should("contain", user.userId);
|
||||
cy.get('[aria-label="User menu"]').click();
|
||||
cy.get(".mx_ContextualMenu").within(() => {
|
||||
cy.get(".mx_UserMenu_contextMenu_displayName").should("contain", "Jeff");
|
||||
cy.get(".mx_UserMenu_contextMenu_userId").should("contain", user.userId);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
import { SynapseInstance } from "../../plugins/synapsedocker";
|
||||
import Chainable = Cypress.Chainable;
|
||||
|
||||
function openCreateRoomDialog(): Chainable<JQuery<HTMLElement>> {
|
||||
cy.get('[aria-label="Add room"]').click();
|
||||
cy.get('.mx_ContextualMenu [aria-label="New room"]').click();
|
||||
return cy.get(".mx_CreateRoomDialog");
|
||||
}
|
||||
|
||||
describe("Create Room", () => {
|
||||
let synapse: SynapseInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.startSynapse("default").then(data => {
|
||||
synapse = data;
|
||||
|
||||
cy.initTestUser(synapse, "Jim");
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopSynapse(synapse);
|
||||
});
|
||||
|
||||
it("should allow us to create a public room with name, topic & address set", () => {
|
||||
const name = "Test room 1";
|
||||
const topic = "This room is dedicated to this test and this test only!";
|
||||
|
||||
openCreateRoomDialog().within(() => {
|
||||
// Fill name & topic
|
||||
cy.get('[label="Name"]').type(name);
|
||||
cy.get('[label="Topic (optional)"]').type(topic);
|
||||
// Change room to public
|
||||
cy.get('[aria-label="Room visibility"]').click();
|
||||
cy.get("#mx_JoinRuleDropdown__public").click();
|
||||
// Fill room address
|
||||
cy.get('[label="Room address"]').type("test-room-1");
|
||||
// Submit
|
||||
cy.get(".mx_Dialog_primary").click();
|
||||
});
|
||||
|
||||
cy.url().should("contain", "/#/room/#test-room-1:localhost");
|
||||
cy.get(".mx_RoomHeader_nametext").contains(name);
|
||||
cy.get(".mx_RoomHeader_topic").contains(topic);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
import { SynapseInstance } from "../../plugins/synapsedocker";
|
||||
import { MatrixClient } from "../../global";
|
||||
|
||||
function markWindowBeforeReload(): void {
|
||||
// mark our window object to "know" when it gets reloaded
|
||||
cy.window().then(w => w.beforeReload = true);
|
||||
}
|
||||
|
||||
describe("Threads", () => {
|
||||
let synapse: SynapseInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.window().then(win => {
|
||||
win.localStorage.setItem("mx_lhs_size", "0"); // Collapse left panel for these tests
|
||||
win.localStorage.setItem("mx_labs_feature_feature_thread", "true"); // Default threads to ON for this spec
|
||||
});
|
||||
cy.startSynapse("default").then(data => {
|
||||
synapse = data;
|
||||
|
||||
cy.initTestUser(synapse, "Tom");
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopSynapse(synapse);
|
||||
});
|
||||
|
||||
it("should reload when enabling threads beta", () => {
|
||||
markWindowBeforeReload();
|
||||
|
||||
// Turn off
|
||||
cy.openUserSettings("Labs").within(() => {
|
||||
// initially the new property is there
|
||||
cy.window().should("have.prop", "beforeReload", true);
|
||||
|
||||
cy.leaveBeta("Threads");
|
||||
// after reload the property should be gone
|
||||
cy.window().should("not.have.prop", "beforeReload");
|
||||
});
|
||||
|
||||
cy.get(".mx_MatrixChat", { timeout: 15000 }); // wait for the app
|
||||
markWindowBeforeReload();
|
||||
|
||||
// Turn on
|
||||
cy.openUserSettings("Labs").within(() => {
|
||||
// initially the new property is there
|
||||
cy.window().should("have.prop", "beforeReload", true);
|
||||
|
||||
cy.joinBeta("Threads");
|
||||
// after reload the property should be gone
|
||||
cy.window().should("not.have.prop", "beforeReload");
|
||||
});
|
||||
});
|
||||
|
||||
it("should be usable for a conversation", () => {
|
||||
let bot: MatrixClient;
|
||||
cy.getBot(synapse, "BotBob").then(_bot => {
|
||||
bot = _bot;
|
||||
});
|
||||
|
||||
let roomId: string;
|
||||
cy.createRoom({}).then(_roomId => {
|
||||
roomId = _roomId;
|
||||
cy.inviteUser(roomId, bot.getUserId());
|
||||
cy.visit("/#/room/" + roomId);
|
||||
});
|
||||
|
||||
// User sends message
|
||||
cy.get(".mx_RoomView_body .mx_BasicMessageComposer_input").type("Hello Mr. Bot{enter}");
|
||||
|
||||
// Wait for message to send, get its ID and save as @threadId
|
||||
cy.get(".mx_RoomView_body .mx_EventTile").contains("Hello Mr. Bot")
|
||||
.closest(".mx_EventTile[data-scroll-tokens]").invoke("attr", "data-scroll-tokens").as("threadId");
|
||||
|
||||
// Bot starts thread
|
||||
cy.get<string>("@threadId").then(threadId => {
|
||||
bot.sendMessage(roomId, threadId, {
|
||||
body: "Hello there",
|
||||
msgtype: "m.text",
|
||||
});
|
||||
});
|
||||
|
||||
// User asserts timeline thread summary visible & clicks it
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_sender").should("contain", "BotBob");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_content").should("contain", "Hello there");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary").click();
|
||||
|
||||
// User responds in thread
|
||||
cy.get(".mx_ThreadView .mx_BasicMessageComposer_input").type("Test{enter}");
|
||||
|
||||
// User asserts summary was updated correctly
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_sender").should("contain", "Tom");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_content").should("contain", "Test");
|
||||
|
||||
// User reacts to message instead
|
||||
cy.get(".mx_ThreadView .mx_EventTile").contains("Hello there").closest(".mx_EventTile_line")
|
||||
.find('[aria-label="React"]').click({ force: true }); // Cypress has no ability to hover
|
||||
cy.get(".mx_EmojiPicker").within(() => {
|
||||
cy.get('input[type="text"]').type("wave");
|
||||
cy.get('[role="menuitem"]').contains("👋").click();
|
||||
});
|
||||
|
||||
// User redacts their prior response
|
||||
cy.get(".mx_ThreadView .mx_EventTile").contains("Test").closest(".mx_EventTile_line")
|
||||
.find('[aria-label="Options"]').click({ force: true }); // Cypress has no ability to hover
|
||||
cy.get(".mx_IconizedContextMenu").within(() => {
|
||||
cy.get('[role="menuitem"]').contains("Remove").click();
|
||||
});
|
||||
cy.get(".mx_TextInputDialog").within(() => {
|
||||
cy.get(".mx_Dialog_primary").contains("Remove").click();
|
||||
});
|
||||
|
||||
// User asserts summary was updated correctly
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_sender").should("contain", "BotBob");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_content").should("contain", "Hello there");
|
||||
|
||||
// User closes right panel after clicking back to thread list
|
||||
cy.get(".mx_ThreadView .mx_BaseCard_back").click();
|
||||
cy.get(".mx_ThreadPanel .mx_BaseCard_close").click();
|
||||
|
||||
// Bot responds to thread
|
||||
cy.get<string>("@threadId").then(threadId => {
|
||||
bot.sendMessage(roomId, threadId, {
|
||||
body: "How are things?",
|
||||
msgtype: "m.text",
|
||||
});
|
||||
});
|
||||
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_sender").should("contain", "BotBob");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_content").should("contain", "How are things?");
|
||||
// User asserts thread list unread indicator
|
||||
cy.get('.mx_HeaderButtons [aria-label="Threads"]').should("have.class", "mx_RightPanel_headerButton_unread");
|
||||
|
||||
// User opens thread list
|
||||
cy.get('.mx_HeaderButtons [aria-label="Threads"]').click();
|
||||
|
||||
// User asserts thread with correct root & latest events & unread dot
|
||||
cy.get(".mx_ThreadPanel .mx_EventTile_last").within(() => {
|
||||
cy.get(".mx_EventTile_body").should("contain", "Hello Mr. Bot");
|
||||
cy.get(".mx_ThreadSummary_content").should("contain", "How are things?");
|
||||
// User opens thread via threads list
|
||||
cy.get(".mx_EventTile_line").click();
|
||||
});
|
||||
|
||||
// User responds & asserts
|
||||
cy.get(".mx_ThreadView .mx_BasicMessageComposer_input").type("Great!{enter}");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_sender").should("contain", "Tom");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_content").should("contain", "Great!");
|
||||
|
||||
// User edits & asserts
|
||||
cy.get(".mx_ThreadView .mx_EventTile_last").contains("Great!").closest(".mx_EventTile_line").within(() => {
|
||||
cy.get('[aria-label="Edit"]').click({ force: true }); // Cypress has no ability to hover
|
||||
cy.get(".mx_BasicMessageComposer_input").type(" How about yourself?{enter}");
|
||||
});
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_sender").should("contain", "Tom");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_content")
|
||||
.should("contain", "Great! How about yourself?");
|
||||
|
||||
// User closes right panel
|
||||
cy.get(".mx_ThreadView .mx_BaseCard_close").click();
|
||||
|
||||
// Bot responds to thread and saves the id of their message to @eventId
|
||||
cy.get<string>("@threadId").then(threadId => {
|
||||
cy.wrap(bot.sendMessage(roomId, threadId, {
|
||||
body: "I'm very good thanks",
|
||||
msgtype: "m.text",
|
||||
}).then(res => res.event_id)).as("eventId");
|
||||
});
|
||||
|
||||
// User asserts
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_sender").should("contain", "BotBob");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_content")
|
||||
.should("contain", "I'm very good thanks");
|
||||
|
||||
// Bot edits their latest event
|
||||
cy.get<string>("@eventId").then(eventId => {
|
||||
bot.sendMessage(roomId, {
|
||||
"body": "* I'm very good thanks :)",
|
||||
"msgtype": "m.text",
|
||||
"m.new_content": {
|
||||
"body": "I'm very good thanks :)",
|
||||
"msgtype": "m.text",
|
||||
},
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.replace",
|
||||
"event_id": eventId,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// User asserts
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_sender").should("contain", "BotBob");
|
||||
cy.get(".mx_RoomView_body .mx_ThreadSummary .mx_ThreadSummary_content")
|
||||
.should("contain", "I'm very good thanks :)");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user