Migrate widgets/* from Cypress to Playwright (#12032)
* Migrate send_event.spec.ts from Cypress to Playwright Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Migrate read_events.spec.ts from Cypress to Playwright Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Migrate kick.spec.ts from Cypress to Playwright Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Migrate get-openid-token.spec.ts from Cypress to Playwright Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Migrate layout.spec.ts from Cypress to Playwright Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Migrate events.spec.ts from Cypress to Playwright Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Migrate stickers.spec.ts from Cypress to Playwright Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Migrate widget-pip-close.spec.ts from Cypress to Playwright Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix types Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Add screenshot Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * expect.poll to stabilise test Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
99ca613818
commit
c9008152c5
20 changed files with 1439 additions and 1563 deletions
|
@ -1,141 +0,0 @@
|
|||
/*
|
||||
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 { HomeserverInstance } from "../../plugins/utils/homeserver";
|
||||
import { UserCredentials } from "../../support/login";
|
||||
|
||||
const ROOM_NAME = "Integration Manager Test";
|
||||
const USER_DISPLAY_NAME = "Alice";
|
||||
|
||||
const INTEGRATION_MANAGER_TOKEN = "DefinitelySecret_DoNotUseThisForReal";
|
||||
const INTEGRATION_MANAGER_HTML = `
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Fake Integration Manager</title>
|
||||
</head>
|
||||
<body>
|
||||
<button name="Send" id="send-action">Press to send action</button>
|
||||
<button name="Close" id="close">Press to close</button>
|
||||
<p id="message-response">No response</p>
|
||||
<script>
|
||||
document.getElementById("send-action").onclick = () => {
|
||||
window.parent.postMessage(
|
||||
{
|
||||
action: "get_open_id_token",
|
||||
},
|
||||
'*',
|
||||
);
|
||||
};
|
||||
document.getElementById("close").onclick = () => {
|
||||
window.parent.postMessage(
|
||||
{
|
||||
action: "close_scalar",
|
||||
},
|
||||
'*',
|
||||
);
|
||||
};
|
||||
// Listen for a postmessage response
|
||||
window.addEventListener("message", (event) => {
|
||||
document.getElementById("message-response").innerText = JSON.stringify(event.data);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
function openIntegrationManager() {
|
||||
cy.findByRole("button", { name: "Room info" }).click();
|
||||
cy.findByRole("button", { name: "Add widgets, bridges & bots" }).click();
|
||||
}
|
||||
|
||||
function sendActionFromIntegrationManager(integrationManagerUrl: string) {
|
||||
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||
cy.findByRole("button", { name: "Press to send action" }).should("exist").click();
|
||||
});
|
||||
}
|
||||
|
||||
describe("Integration Manager: Get OpenID Token", () => {
|
||||
let testUser: UserCredentials;
|
||||
let homeserver: HomeserverInstance;
|
||||
let integrationManagerUrl: string;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.serveHtmlFile(INTEGRATION_MANAGER_HTML).then((url) => {
|
||||
integrationManagerUrl = url;
|
||||
});
|
||||
cy.startHomeserver("default").then((data) => {
|
||||
homeserver = data;
|
||||
|
||||
cy.initTestUser(homeserver, USER_DISPLAY_NAME, () => {
|
||||
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) => {
|
||||
testUser = user;
|
||||
});
|
||||
|
||||
cy.setAccountData("m.widgets", {
|
||||
"m.integration_manager": {
|
||||
content: {
|
||||
type: "m.integration_manager",
|
||||
name: "Integration Manager",
|
||||
url: integrationManagerUrl,
|
||||
data: {
|
||||
api_url: integrationManagerUrl,
|
||||
},
|
||||
},
|
||||
id: "integration-manager",
|
||||
},
|
||||
}).as("integrationManager");
|
||||
|
||||
// Succeed when checking the token is valid
|
||||
cy.intercept(`${integrationManagerUrl}/account?scalar_token=${INTEGRATION_MANAGER_TOKEN}*`, (req) => {
|
||||
req.continue((res) => {
|
||||
return res.send(200, {
|
||||
user_id: testUser.userId,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
cy.createRoom({
|
||||
name: ROOM_NAME,
|
||||
}).as("roomId");
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopHomeserver(homeserver);
|
||||
cy.stopWebServers();
|
||||
});
|
||||
|
||||
it("should successfully obtain an openID token", () => {
|
||||
cy.all([cy.get<{}>("@integrationManager")]).then(() => {
|
||||
cy.viewRoomByName(ROOM_NAME);
|
||||
|
||||
openIntegrationManager();
|
||||
sendActionFromIntegrationManager(integrationManagerUrl);
|
||||
|
||||
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||
cy.get("#message-response").within(() => {
|
||||
cy.findByText(/access_token/);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,265 +0,0 @@
|
|||
/*
|
||||
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 { HomeserverInstance } from "../../plugins/utils/homeserver";
|
||||
import { MatrixClient } from "../../global";
|
||||
import { UserCredentials } from "../../support/login";
|
||||
|
||||
const ROOM_NAME = "Integration Manager Test";
|
||||
const USER_DISPLAY_NAME = "Alice";
|
||||
const BOT_DISPLAY_NAME = "Bob";
|
||||
const KICK_REASON = "Goodbye";
|
||||
|
||||
const INTEGRATION_MANAGER_TOKEN = "DefinitelySecret_DoNotUseThisForReal";
|
||||
const INTEGRATION_MANAGER_HTML = `
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Fake Integration Manager</title>
|
||||
</head>
|
||||
<body>
|
||||
<input type="text" id="target-room-id"/>
|
||||
<input type="text" id="target-user-id"/>
|
||||
<button name="Send" id="send-action">Press to send action</button>
|
||||
<button name="Close" id="close">Press to close</button>
|
||||
<script>
|
||||
document.getElementById("send-action").onclick = () => {
|
||||
window.parent.postMessage(
|
||||
{
|
||||
action: "kick",
|
||||
room_id: document.getElementById("target-room-id").value,
|
||||
user_id: document.getElementById("target-user-id").value,
|
||||
reason: "${KICK_REASON}",
|
||||
},
|
||||
'*',
|
||||
);
|
||||
};
|
||||
document.getElementById("close").onclick = () => {
|
||||
window.parent.postMessage(
|
||||
{
|
||||
action: "close_scalar",
|
||||
},
|
||||
'*',
|
||||
);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
function openIntegrationManager() {
|
||||
cy.findByRole("button", { name: "Room info" }).click();
|
||||
cy.findByRole("button", { name: "Add widgets, bridges & bots" }).click();
|
||||
}
|
||||
|
||||
function closeIntegrationManager(integrationManagerUrl: string) {
|
||||
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||
cy.findByRole("button", { name: "Press to close" }).should("exist").click();
|
||||
});
|
||||
}
|
||||
|
||||
function sendActionFromIntegrationManager(integrationManagerUrl: string, targetRoomId: string, targetUserId: string) {
|
||||
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||
cy.get("#target-room-id").should("exist").type(targetRoomId);
|
||||
cy.get("#target-user-id").should("exist").type(targetUserId);
|
||||
cy.findByRole("button", { name: "Press to send action" }).should("exist").click();
|
||||
});
|
||||
}
|
||||
|
||||
function clickUntilGone(selector: string, attempt = 0) {
|
||||
if (attempt === 11) {
|
||||
throw new Error("clickUntilGone attempt count exceeded");
|
||||
}
|
||||
|
||||
cy.get(selector)
|
||||
.last()
|
||||
.click()
|
||||
.then(($button) => {
|
||||
const exists = Cypress.$(selector).length > 0;
|
||||
if (exists) {
|
||||
clickUntilGone(selector, ++attempt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function expectKickedMessage(shouldExist: boolean) {
|
||||
// Expand any event summaries, we can't use a click multiple here because clicking one might de-render others
|
||||
// This is quite horrible but seems the most stable way of clicking 0-N buttons,
|
||||
// one at a time with a full re-evaluation after each click
|
||||
clickUntilGone(".mx_GenericEventListSummary_toggle[aria-expanded=false]");
|
||||
|
||||
// Check for the event message (or lack thereof)
|
||||
cy.findByText(`${USER_DISPLAY_NAME} removed ${BOT_DISPLAY_NAME}: ${KICK_REASON}`).should(
|
||||
shouldExist ? "exist" : "not.exist",
|
||||
);
|
||||
}
|
||||
|
||||
describe("Integration Manager: Kick", () => {
|
||||
let testUser: UserCredentials;
|
||||
let homeserver: HomeserverInstance;
|
||||
let integrationManagerUrl: string;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.serveHtmlFile(INTEGRATION_MANAGER_HTML).then((url) => {
|
||||
integrationManagerUrl = url;
|
||||
});
|
||||
cy.startHomeserver("default").then((data) => {
|
||||
homeserver = data;
|
||||
|
||||
cy.initTestUser(homeserver, USER_DISPLAY_NAME, () => {
|
||||
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) => {
|
||||
testUser = user;
|
||||
});
|
||||
|
||||
cy.setAccountData("m.widgets", {
|
||||
"m.integration_manager": {
|
||||
content: {
|
||||
type: "m.integration_manager",
|
||||
name: "Integration Manager",
|
||||
url: integrationManagerUrl,
|
||||
data: {
|
||||
api_url: integrationManagerUrl,
|
||||
},
|
||||
},
|
||||
id: "integration-manager",
|
||||
},
|
||||
}).as("integrationManager");
|
||||
|
||||
// Succeed when checking the token is valid
|
||||
cy.intercept(`${integrationManagerUrl}/account?scalar_token=${INTEGRATION_MANAGER_TOKEN}*`, (req) => {
|
||||
req.continue((res) => {
|
||||
return res.send(200, {
|
||||
user_id: testUser.userId,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
cy.createRoom({
|
||||
name: ROOM_NAME,
|
||||
}).as("roomId");
|
||||
|
||||
cy.getBot(homeserver, { displayName: BOT_DISPLAY_NAME, autoAcceptInvites: true }).as("bob");
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopHomeserver(homeserver);
|
||||
cy.stopWebServers();
|
||||
});
|
||||
|
||||
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.findByText(`${BOT_DISPLAY_NAME} joined the room`).should("exist");
|
||||
|
||||
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.findByText(`${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.findByText(`${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.findByText(`${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);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1,276 +0,0 @@
|
|||
/*
|
||||
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 { HomeserverInstance } from "../../plugins/utils/homeserver";
|
||||
import { UserCredentials } from "../../support/login";
|
||||
|
||||
const ROOM_NAME = "Integration Manager Test";
|
||||
const USER_DISPLAY_NAME = "Alice";
|
||||
|
||||
const INTEGRATION_MANAGER_TOKEN = "DefinitelySecret_DoNotUseThisForReal";
|
||||
const INTEGRATION_MANAGER_HTML = `
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Fake Integration Manager</title>
|
||||
</head>
|
||||
<body>
|
||||
<input type="text" id="target-room-id"/>
|
||||
<input type="text" id="event-type"/>
|
||||
<input type="text" id="state-key"/>
|
||||
<button name="Send" id="send-action">Press to send action</button>
|
||||
<button name="Close" id="close">Press to close</button>
|
||||
<p id="message-response">No response</p>
|
||||
<script>
|
||||
document.getElementById("send-action").onclick = () => {
|
||||
window.parent.postMessage(
|
||||
{
|
||||
action: "read_events",
|
||||
room_id: document.getElementById("target-room-id").value,
|
||||
type: document.getElementById("event-type").value,
|
||||
state_key: JSON.parse(document.getElementById("state-key").value),
|
||||
},
|
||||
'*',
|
||||
);
|
||||
};
|
||||
document.getElementById("close").onclick = () => {
|
||||
window.parent.postMessage(
|
||||
{
|
||||
action: "close_scalar",
|
||||
},
|
||||
'*',
|
||||
);
|
||||
};
|
||||
// Listen for a postmessage response
|
||||
window.addEventListener("message", (event) => {
|
||||
document.getElementById("message-response").innerText = JSON.stringify(event.data);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
function openIntegrationManager() {
|
||||
cy.findByRole("button", { name: "Room info" }).click();
|
||||
cy.get(".mx_RoomSummaryCard_appsGroup").within(() => {
|
||||
cy.findByRole("button", { name: "Add widgets, bridges & bots" }).click();
|
||||
});
|
||||
}
|
||||
|
||||
function sendActionFromIntegrationManager(
|
||||
integrationManagerUrl: string,
|
||||
targetRoomId: string,
|
||||
eventType: string,
|
||||
stateKey: string | boolean,
|
||||
) {
|
||||
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||
cy.get("#target-room-id").should("exist").type(targetRoomId);
|
||||
cy.get("#event-type").should("exist").type(eventType);
|
||||
cy.get("#state-key").should("exist").type(JSON.stringify(stateKey));
|
||||
cy.get("#send-action").should("exist").click();
|
||||
});
|
||||
}
|
||||
|
||||
describe("Integration Manager: Read Events", () => {
|
||||
let testUser: UserCredentials;
|
||||
let homeserver: HomeserverInstance;
|
||||
let integrationManagerUrl: string;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.serveHtmlFile(INTEGRATION_MANAGER_HTML).then((url) => {
|
||||
integrationManagerUrl = url;
|
||||
});
|
||||
cy.startHomeserver("default").then((data) => {
|
||||
homeserver = data;
|
||||
|
||||
cy.initTestUser(homeserver, USER_DISPLAY_NAME, () => {
|
||||
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) => {
|
||||
testUser = user;
|
||||
});
|
||||
|
||||
cy.setAccountData("m.widgets", {
|
||||
"m.integration_manager": {
|
||||
content: {
|
||||
type: "m.integration_manager",
|
||||
name: "Integration Manager",
|
||||
url: integrationManagerUrl,
|
||||
data: {
|
||||
api_url: integrationManagerUrl,
|
||||
},
|
||||
},
|
||||
id: "integration-manager",
|
||||
},
|
||||
}).as("integrationManager");
|
||||
|
||||
// Succeed when checking the token is valid
|
||||
cy.intercept(`${integrationManagerUrl}/account?scalar_token=${INTEGRATION_MANAGER_TOKEN}*`, (req) => {
|
||||
req.continue((res) => {
|
||||
return res.send(200, {
|
||||
user_id: testUser.userId,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
cy.createRoom({
|
||||
name: ROOM_NAME,
|
||||
}).as("roomId");
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopHomeserver(homeserver);
|
||||
cy.stopWebServers();
|
||||
});
|
||||
|
||||
it("should read a state event by state key", () => {
|
||||
cy.all([cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(([roomId]) => {
|
||||
cy.viewRoomByName(ROOM_NAME);
|
||||
|
||||
const eventType = "io.element.integrations.installations";
|
||||
const eventContent = {
|
||||
foo: "bar",
|
||||
};
|
||||
const stateKey = "state-key-123";
|
||||
|
||||
// Send a state event
|
||||
cy.getClient()
|
||||
.then(async (client) => {
|
||||
return await client.sendStateEvent(roomId, eventType, eventContent, stateKey);
|
||||
})
|
||||
.then((event) => {
|
||||
openIntegrationManager();
|
||||
|
||||
// Read state events
|
||||
sendActionFromIntegrationManager(integrationManagerUrl, roomId, eventType, stateKey);
|
||||
|
||||
// Check the response
|
||||
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||
cy.get("#message-response")
|
||||
.should("include.text", event.event_id)
|
||||
.should("include.text", `"content":${JSON.stringify(eventContent)}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should read a state event with empty state key", () => {
|
||||
cy.all([cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(([roomId]) => {
|
||||
cy.viewRoomByName(ROOM_NAME);
|
||||
|
||||
const eventType = "io.element.integrations.installations";
|
||||
const eventContent = {
|
||||
foo: "bar",
|
||||
};
|
||||
const stateKey = "";
|
||||
|
||||
// Send a state event
|
||||
cy.getClient()
|
||||
.then(async (client) => {
|
||||
return await client.sendStateEvent(roomId, eventType, eventContent, stateKey);
|
||||
})
|
||||
.then((event) => {
|
||||
openIntegrationManager();
|
||||
|
||||
// Read state events
|
||||
sendActionFromIntegrationManager(integrationManagerUrl, roomId, eventType, stateKey);
|
||||
|
||||
// Check the response
|
||||
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||
cy.get("#message-response")
|
||||
.should("include.text", event.event_id)
|
||||
.should("include.text", `"content":${JSON.stringify(eventContent)}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should read state events with any state key", () => {
|
||||
cy.all([cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(([roomId]) => {
|
||||
cy.viewRoomByName(ROOM_NAME);
|
||||
|
||||
const eventType = "io.element.integrations.installations";
|
||||
|
||||
const stateKey1 = "state-key-123";
|
||||
const eventContent1 = {
|
||||
foo1: "bar1",
|
||||
};
|
||||
const stateKey2 = "state-key-456";
|
||||
const eventContent2 = {
|
||||
foo2: "bar2",
|
||||
};
|
||||
const stateKey3 = "state-key-789";
|
||||
const eventContent3 = {
|
||||
foo3: "bar3",
|
||||
};
|
||||
|
||||
// Send state events
|
||||
cy.getClient()
|
||||
.then(async (client) => {
|
||||
return Promise.all([
|
||||
client.sendStateEvent(roomId, eventType, eventContent1, stateKey1),
|
||||
client.sendStateEvent(roomId, eventType, eventContent2, stateKey2),
|
||||
client.sendStateEvent(roomId, eventType, eventContent3, stateKey3),
|
||||
]);
|
||||
})
|
||||
.then((events) => {
|
||||
openIntegrationManager();
|
||||
|
||||
// Read state events
|
||||
sendActionFromIntegrationManager(
|
||||
integrationManagerUrl,
|
||||
roomId,
|
||||
eventType,
|
||||
true, // Any state key
|
||||
);
|
||||
|
||||
// Check the response
|
||||
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||
cy.get("#message-response")
|
||||
.should("include.text", events[0].event_id)
|
||||
.should("include.text", `"content":${JSON.stringify(eventContent1)}`)
|
||||
.should("include.text", events[1].event_id)
|
||||
.should("include.text", `"content":${JSON.stringify(eventContent2)}`)
|
||||
.should("include.text", events[2].event_id)
|
||||
.should("include.text", `"content":${JSON.stringify(eventContent3)}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should fail to read an event type which is not allowed", () => {
|
||||
cy.all([cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(([roomId]) => {
|
||||
cy.viewRoomByName(ROOM_NAME);
|
||||
|
||||
const eventType = "com.example.event";
|
||||
const stateKey = "";
|
||||
|
||||
openIntegrationManager();
|
||||
|
||||
// Read state events
|
||||
sendActionFromIntegrationManager(integrationManagerUrl, roomId, eventType, stateKey);
|
||||
|
||||
// Check the response
|
||||
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||
cy.get("#message-response").should("include.text", "Failed to read events");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,261 +0,0 @@
|
|||
/*
|
||||
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 { HomeserverInstance } from "../../plugins/utils/homeserver";
|
||||
import { UserCredentials } from "../../support/login";
|
||||
|
||||
const ROOM_NAME = "Integration Manager Test";
|
||||
const USER_DISPLAY_NAME = "Alice";
|
||||
|
||||
const INTEGRATION_MANAGER_TOKEN = "DefinitelySecret_DoNotUseThisForReal";
|
||||
const INTEGRATION_MANAGER_HTML = `
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Fake Integration Manager</title>
|
||||
</head>
|
||||
<body>
|
||||
<input type="text" id="target-room-id"/>
|
||||
<input type="text" id="event-type"/>
|
||||
<input type="text" id="state-key"/>
|
||||
<input type="text" id="event-content"/>
|
||||
<button name="Send" id="send-action">Press to send action</button>
|
||||
<button name="Close" id="close">Press to close</button>
|
||||
<p id="message-response">No response</p>
|
||||
<script>
|
||||
document.getElementById("send-action").onclick = () => {
|
||||
window.parent.postMessage(
|
||||
{
|
||||
action: "send_event",
|
||||
room_id: document.getElementById("target-room-id").value,
|
||||
type: document.getElementById("event-type").value,
|
||||
state_key: document.getElementById("state-key").value,
|
||||
content: JSON.parse(document.getElementById("event-content").value),
|
||||
},
|
||||
'*',
|
||||
);
|
||||
};
|
||||
document.getElementById("close").onclick = () => {
|
||||
window.parent.postMessage(
|
||||
{
|
||||
action: "close_scalar",
|
||||
},
|
||||
'*',
|
||||
);
|
||||
};
|
||||
// Listen for a postmessage response
|
||||
window.addEventListener("message", (event) => {
|
||||
document.getElementById("message-response").innerText = JSON.stringify(event.data);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
function openIntegrationManager() {
|
||||
cy.findByRole("button", { name: "Room info" }).click();
|
||||
cy.get(".mx_RoomSummaryCard_appsGroup").within(() => {
|
||||
cy.findByRole("button", { name: "Add widgets, bridges & bots" }).click();
|
||||
});
|
||||
}
|
||||
|
||||
function sendActionFromIntegrationManager(
|
||||
integrationManagerUrl: string,
|
||||
targetRoomId: string,
|
||||
eventType: string,
|
||||
stateKey: string,
|
||||
content: Record<string, unknown>,
|
||||
) {
|
||||
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||
cy.get("#target-room-id").should("exist").type(targetRoomId);
|
||||
cy.get("#event-type").should("exist").type(eventType);
|
||||
if (stateKey) {
|
||||
cy.get("#state-key").should("exist").type(stateKey);
|
||||
}
|
||||
cy.get("#event-content").should("exist").type(JSON.stringify(content), { parseSpecialCharSequences: false });
|
||||
cy.get("#send-action").should("exist").click();
|
||||
});
|
||||
}
|
||||
|
||||
describe("Integration Manager: Send Event", () => {
|
||||
let testUser: UserCredentials;
|
||||
let homeserver: HomeserverInstance;
|
||||
let integrationManagerUrl: string;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.serveHtmlFile(INTEGRATION_MANAGER_HTML).then((url) => {
|
||||
integrationManagerUrl = url;
|
||||
});
|
||||
cy.startHomeserver("default").then((data) => {
|
||||
homeserver = data;
|
||||
|
||||
cy.initTestUser(homeserver, USER_DISPLAY_NAME, () => {
|
||||
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) => {
|
||||
testUser = user;
|
||||
});
|
||||
|
||||
cy.setAccountData("m.widgets", {
|
||||
"m.integration_manager": {
|
||||
content: {
|
||||
type: "m.integration_manager",
|
||||
name: "Integration Manager",
|
||||
url: integrationManagerUrl,
|
||||
data: {
|
||||
api_url: integrationManagerUrl,
|
||||
},
|
||||
},
|
||||
id: "integration-manager",
|
||||
},
|
||||
}).as("integrationManager");
|
||||
|
||||
// Succeed when checking the token is valid
|
||||
cy.intercept(`${integrationManagerUrl}/account?scalar_token=${INTEGRATION_MANAGER_TOKEN}*`, (req) => {
|
||||
req.continue((res) => {
|
||||
return res.send(200, {
|
||||
user_id: testUser.userId,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
cy.createRoom({
|
||||
name: ROOM_NAME,
|
||||
}).as("roomId");
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopHomeserver(homeserver);
|
||||
cy.stopWebServers();
|
||||
});
|
||||
|
||||
it("should send a state event", () => {
|
||||
cy.all([cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(([roomId]) => {
|
||||
cy.viewRoomByName(ROOM_NAME);
|
||||
|
||||
openIntegrationManager();
|
||||
|
||||
const eventType = "io.element.integrations.installations";
|
||||
const eventContent = {
|
||||
foo: "bar",
|
||||
};
|
||||
const stateKey = "state-key-123";
|
||||
|
||||
// Send the event
|
||||
sendActionFromIntegrationManager(integrationManagerUrl, roomId, eventType, stateKey, eventContent);
|
||||
|
||||
// Check the response
|
||||
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||
cy.get("#message-response").should("include.text", "event_id");
|
||||
});
|
||||
|
||||
// Check the event
|
||||
cy.getClient()
|
||||
.then(async (client) => {
|
||||
return await client.getStateEvent(roomId, eventType, stateKey);
|
||||
})
|
||||
.then((event) => {
|
||||
expect(event).to.deep.equal(eventContent);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should send a state event with empty content", () => {
|
||||
cy.all([cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(([roomId]) => {
|
||||
cy.viewRoomByName(ROOM_NAME);
|
||||
|
||||
openIntegrationManager();
|
||||
|
||||
const eventType = "io.element.integrations.installations";
|
||||
const eventContent = {};
|
||||
const stateKey = "state-key-123";
|
||||
|
||||
// Send the event
|
||||
sendActionFromIntegrationManager(integrationManagerUrl, roomId, eventType, stateKey, eventContent);
|
||||
|
||||
// Check the response
|
||||
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||
cy.get("#message-response").should("include.text", "event_id");
|
||||
});
|
||||
|
||||
// Check the event
|
||||
cy.getClient()
|
||||
.then(async (client) => {
|
||||
return await client.getStateEvent(roomId, eventType, stateKey);
|
||||
})
|
||||
.then((event) => {
|
||||
expect(event).to.be.empty;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should send a state event with empty state key", () => {
|
||||
cy.all([cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(([roomId]) => {
|
||||
cy.viewRoomByName(ROOM_NAME);
|
||||
|
||||
openIntegrationManager();
|
||||
|
||||
const eventType = "io.element.integrations.installations";
|
||||
const eventContent = {
|
||||
foo: "bar",
|
||||
};
|
||||
const stateKey = "";
|
||||
|
||||
// Send the event
|
||||
sendActionFromIntegrationManager(integrationManagerUrl, roomId, eventType, stateKey, eventContent);
|
||||
|
||||
// Check the response
|
||||
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||
cy.get("#message-response").should("include.text", "event_id");
|
||||
});
|
||||
|
||||
// Check the event
|
||||
cy.getClient()
|
||||
.then(async (client) => {
|
||||
return await client.getStateEvent(roomId, eventType, stateKey);
|
||||
})
|
||||
.then((event) => {
|
||||
expect(event).to.deep.equal(eventContent);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should fail to send an event type which is not allowed", () => {
|
||||
cy.all([cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(([roomId]) => {
|
||||
cy.viewRoomByName(ROOM_NAME);
|
||||
|
||||
openIntegrationManager();
|
||||
|
||||
const eventType = "com.example.event";
|
||||
const eventContent = {
|
||||
foo: "bar",
|
||||
};
|
||||
const stateKey = "";
|
||||
|
||||
// Send the event
|
||||
sendActionFromIntegrationManager(integrationManagerUrl, roomId, eventType, stateKey, eventContent);
|
||||
|
||||
// Check the response
|
||||
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||
cy.get("#message-response").should("include.text", "Failed to send event");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,200 +0,0 @@
|
|||
/*
|
||||
Copyright 2022 Mikhail Aheichyk
|
||||
Copyright 2022 Nordeck IT + Consulting GmbH.
|
||||
|
||||
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 { IWidget } from "matrix-widget-api/src/interfaces/IWidget";
|
||||
|
||||
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { HomeserverInstance } from "../../plugins/utils/homeserver";
|
||||
import { UserCredentials } from "../../support/login";
|
||||
import { waitForRoom } from "../utils";
|
||||
|
||||
const DEMO_WIDGET_ID = "demo-widget-id";
|
||||
const DEMO_WIDGET_NAME = "Demo Widget";
|
||||
const DEMO_WIDGET_TYPE = "demo";
|
||||
const ROOM_NAME = "Demo";
|
||||
|
||||
const DEMO_WIDGET_HTML = `
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Demo Widget</title>
|
||||
<script>
|
||||
let sendEventCount = 0
|
||||
window.onmessage = ev => {
|
||||
if (ev.data.action === 'capabilities') {
|
||||
window.parent.postMessage(Object.assign({
|
||||
response: {
|
||||
capabilities: [
|
||||
"org.matrix.msc2762.timeline:*",
|
||||
"org.matrix.msc2762.receive.state_event:m.room.topic",
|
||||
"org.matrix.msc2762.send.event:net.widget_echo"
|
||||
]
|
||||
},
|
||||
}, ev.data), '*');
|
||||
} else if (ev.data.action === 'send_event' && !ev.data.response) {
|
||||
// wraps received event into 'net.widget_echo' and sends back
|
||||
sendEventCount += 1
|
||||
window.parent.postMessage({
|
||||
api: "fromWidget",
|
||||
widgetId: ev.data.widgetId,
|
||||
requestId: 'widget-' + sendEventCount,
|
||||
action: "send_event",
|
||||
data: {
|
||||
type: 'net.widget_echo',
|
||||
content: ev.data.data // sets matrix event to the content returned
|
||||
},
|
||||
}, '*')
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<button id="demo">Demo</button>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
describe("Widget Events", () => {
|
||||
let homeserver: HomeserverInstance;
|
||||
let user: UserCredentials;
|
||||
let bot: MatrixClient;
|
||||
let demoWidgetUrl: string;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.startHomeserver("default").then((data) => {
|
||||
homeserver = data;
|
||||
|
||||
cy.initTestUser(homeserver, "Mike").then((_user) => {
|
||||
user = _user;
|
||||
});
|
||||
cy.getBot(homeserver, { displayName: "Bot", autoAcceptInvites: true }).then((_bot) => {
|
||||
bot = _bot;
|
||||
});
|
||||
});
|
||||
cy.serveHtmlFile(DEMO_WIDGET_HTML).then((url) => {
|
||||
demoWidgetUrl = url;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopHomeserver(homeserver);
|
||||
cy.stopWebServers();
|
||||
});
|
||||
|
||||
it("should be updated if user is re-invited into the room with updated state event", () => {
|
||||
cy.createRoom({
|
||||
name: ROOM_NAME,
|
||||
invite: [bot.getUserId()],
|
||||
}).then((roomId) => {
|
||||
// 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");
|
||||
|
||||
// set initial layout
|
||||
cy.getClient()
|
||||
.then(async (matrixClient) => {
|
||||
const content = {
|
||||
widgets: {
|
||||
[DEMO_WIDGET_ID]: {
|
||||
container: "top",
|
||||
index: 1,
|
||||
width: 100,
|
||||
height: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
await matrixClient.sendStateEvent(roomId, "io.element.widgets.layout", content, "");
|
||||
})
|
||||
.as("layoutEventSent");
|
||||
|
||||
// open the room
|
||||
cy.viewRoomByName(ROOM_NAME);
|
||||
|
||||
// approve capabilities
|
||||
cy.get(".mx_WidgetCapabilitiesPromptDialog").within(() => {
|
||||
cy.findByRole("button", { name: "Approve" }).click();
|
||||
});
|
||||
|
||||
cy.all([cy.get<string>("@widgetEventSent"), cy.get<string>("@layoutEventSent")]).then(async () => {
|
||||
// bot creates a new room with 'm.room.topic'
|
||||
const { room_id: roomNew } = await bot.createRoom({
|
||||
name: "New room",
|
||||
initial_state: [
|
||||
{
|
||||
type: "m.room.topic",
|
||||
state_key: "",
|
||||
content: {
|
||||
topic: "topic initial",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await bot.invite(roomNew, user.userId);
|
||||
|
||||
// widget should receive 'm.room.topic' event after invite
|
||||
cy.window().then(async (win) => {
|
||||
await waitForRoom(win, win.mxMatrixClientPeg.get(), roomId, (room) => {
|
||||
const events = room.getLiveTimeline().getEvents();
|
||||
return events.some(
|
||||
(e) =>
|
||||
e.getType() === "net.widget_echo" &&
|
||||
e.getContent().type === "m.room.topic" &&
|
||||
e.getContent().content.topic === "topic initial",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// update the topic
|
||||
await bot.sendStateEvent(
|
||||
roomNew,
|
||||
"m.room.topic",
|
||||
{
|
||||
topic: "topic updated",
|
||||
},
|
||||
"",
|
||||
);
|
||||
|
||||
await bot.invite(roomNew, user.userId, "something changed in the room");
|
||||
|
||||
// widget should receive updated 'm.room.topic' event after re-invite
|
||||
cy.window().then(async (win) => {
|
||||
await waitForRoom(win, win.mxMatrixClientPeg.get(), roomId, (room) => {
|
||||
const events = room.getLiveTimeline().getEvents();
|
||||
return events.some(
|
||||
(e) =>
|
||||
e.getType() === "net.widget_echo" &&
|
||||
e.getContent().type === "m.room.topic" &&
|
||||
e.getContent().content.topic === "topic updated",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,132 +0,0 @@
|
|||
/*
|
||||
Copyright 2022 Oliver Sand
|
||||
Copyright 2022 Nordeck IT + Consulting GmbH.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IWidget } from "matrix-widget-api";
|
||||
|
||||
import { HomeserverInstance } from "../../plugins/utils/homeserver";
|
||||
|
||||
const ROOM_NAME = "Test Room";
|
||||
const WIDGET_ID = "fake-widget";
|
||||
const WIDGET_HTML = `
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Fake Widget</title>
|
||||
</head>
|
||||
<body>
|
||||
Hello World
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
describe("Widget Layout", () => {
|
||||
let widgetUrl: string;
|
||||
let homeserver: HomeserverInstance;
|
||||
let roomId: string;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.startHomeserver("default").then((data) => {
|
||||
homeserver = data;
|
||||
|
||||
cy.initTestUser(homeserver, "Sally");
|
||||
});
|
||||
cy.serveHtmlFile(WIDGET_HTML).then((url) => {
|
||||
widgetUrl = url;
|
||||
});
|
||||
|
||||
cy.createRoom({
|
||||
name: ROOM_NAME,
|
||||
}).then((id) => {
|
||||
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");
|
||||
|
||||
// set initial layout
|
||||
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");
|
||||
});
|
||||
|
||||
cy.all([cy.get<string>("@widgetEventSent"), cy.get<string>("@layoutEventSent")]).then(() => {
|
||||
// open the room
|
||||
cy.viewRoomByName(ROOM_NAME);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopHomeserver(homeserver);
|
||||
cy.stopWebServers();
|
||||
});
|
||||
|
||||
it("should be set properly", () => {
|
||||
cy.get(".mx_AppsDrawer").percySnapshotElement("Widgets drawer on the timeline (AppsDrawer)");
|
||||
});
|
||||
|
||||
it("manually resize the height of the top container layout", () => {
|
||||
cy.get('iframe[title="widget"]').invoke("height").should("be.lessThan", 250);
|
||||
|
||||
cy.get(".mx_AppsDrawer_resizer_container_handle")
|
||||
.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);
|
||||
});
|
||||
|
||||
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) => {
|
||||
const content = {
|
||||
widgets: {
|
||||
[WIDGET_ID]: {
|
||||
container: "top",
|
||||
index: 1,
|
||||
width: 100,
|
||||
height: 100,
|
||||
},
|
||||
},
|
||||
};
|
||||
await matrixClient.sendStateEvent(roomId, "io.element.widgets.layout", content, "");
|
||||
});
|
||||
|
||||
cy.get('iframe[title="widget"]').invoke("height").should("be.greaterThan", 400);
|
||||
});
|
||||
});
|
|
@ -1,193 +0,0 @@
|
|||
/*
|
||||
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 { HomeserverInstance } from "../../plugins/utils/homeserver";
|
||||
|
||||
const STICKER_PICKER_WIDGET_ID = "fake-sticker-picker";
|
||||
const STICKER_PICKER_WIDGET_NAME = "Fake Stickers";
|
||||
const STICKER_NAME = "Test Sticker";
|
||||
const ROOM_NAME_1 = "Sticker Test";
|
||||
const ROOM_NAME_2 = "Sticker Test Two";
|
||||
const STICKER_MESSAGE = JSON.stringify({
|
||||
action: "m.sticker",
|
||||
api: "fromWidget",
|
||||
data: {
|
||||
name: "teststicker",
|
||||
description: STICKER_NAME,
|
||||
file: "test.png",
|
||||
content: {
|
||||
body: STICKER_NAME,
|
||||
msgtype: "m.sticker",
|
||||
url: "mxc://somewhere",
|
||||
},
|
||||
},
|
||||
requestId: "1",
|
||||
widgetId: STICKER_PICKER_WIDGET_ID,
|
||||
});
|
||||
const WIDGET_HTML = `
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Fake Sticker Picker</title>
|
||||
<script>
|
||||
window.onmessage = ev => {
|
||||
if (ev.data.action === 'capabilities') {
|
||||
window.parent.postMessage(Object.assign({
|
||||
response: {
|
||||
capabilities: ["m.sticker"]
|
||||
},
|
||||
}, ev.data), '*');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<button name="Send" id="sendsticker">Press for sticker</button>
|
||||
<script>
|
||||
document.getElementById('sendsticker').onclick = () => {
|
||||
window.parent.postMessage(${STICKER_MESSAGE}, '*')
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
function openStickerPicker() {
|
||||
cy.openMessageComposerOptions().findByRole("menuitem", { name: "Sticker" }).click();
|
||||
}
|
||||
|
||||
function sendStickerFromPicker() {
|
||||
// Note: Until https://github.com/cypress-io/cypress/issues/136 is fixed we will need
|
||||
// to use `chromeWebSecurity: false` in our cypress config. Not even cy.origin() can
|
||||
// break into the iframe for us :(
|
||||
cy.accessIframe(`iframe[title="${STICKER_PICKER_WIDGET_NAME}"]`).within({}, () => {
|
||||
cy.get("#sendsticker").should("exist").click();
|
||||
});
|
||||
|
||||
// Sticker picker should close itself after sending.
|
||||
cy.get(".mx_AppTileFullWidth#stickers").should("not.exist");
|
||||
}
|
||||
|
||||
function expectTimelineSticker(roomId: string) {
|
||||
// Make sure it's in the right room
|
||||
cy.get(".mx_EventTile_sticker > a").should("have.attr", "href").and("include", `/${roomId}/`);
|
||||
|
||||
// Make sure the image points at the sticker image. We will briefly show it
|
||||
// using the thumbnail URL, but as soon as that fails, we will switch to the
|
||||
// download URL.
|
||||
cy.get<HTMLImageElement>(`img[alt="${STICKER_NAME}"][src*="download/somewhere"]`).should("exist");
|
||||
}
|
||||
|
||||
describe("Stickers", () => {
|
||||
// We spin up a web server for the sticker picker so that we're not testing to see if
|
||||
// sysadmins can deploy sticker pickers on the same Element domain - we actually want
|
||||
// to make sure that cross-origin postMessage works properly. This makes it difficult
|
||||
// to write the test though, as we have to juggle iframe logistics.
|
||||
//
|
||||
// See sendStickerFromPicker() for more detail on iframe comms.
|
||||
|
||||
let stickerPickerUrl: string;
|
||||
let homeserver: HomeserverInstance;
|
||||
let userId: string;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.startHomeserver("default").then((data) => {
|
||||
homeserver = data;
|
||||
|
||||
cy.initTestUser(homeserver, "Sally").then((user) => (userId = user.userId));
|
||||
});
|
||||
cy.serveHtmlFile(WIDGET_HTML).then((url) => {
|
||||
stickerPickerUrl = url;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopHomeserver(homeserver);
|
||||
cy.stopWebServers();
|
||||
});
|
||||
|
||||
it("should send a sticker to multiple rooms", () => {
|
||||
cy.createRoom({
|
||||
name: ROOM_NAME_1,
|
||||
}).as("roomId1");
|
||||
cy.createRoom({
|
||||
name: ROOM_NAME_2,
|
||||
}).as("roomId2");
|
||||
cy.setAccountData("m.widgets", {
|
||||
[STICKER_PICKER_WIDGET_ID]: {
|
||||
content: {
|
||||
type: "m.stickerpicker",
|
||||
name: STICKER_PICKER_WIDGET_NAME,
|
||||
url: stickerPickerUrl,
|
||||
creatorUserId: userId,
|
||||
},
|
||||
sender: userId,
|
||||
state_key: STICKER_PICKER_WIDGET_ID,
|
||||
type: "m.widget",
|
||||
id: STICKER_PICKER_WIDGET_ID,
|
||||
},
|
||||
}).as("stickers");
|
||||
|
||||
cy.all([
|
||||
cy.get<string>("@roomId1"),
|
||||
cy.get<string>("@roomId2"),
|
||||
cy.get<{}>("@stickers"), // just want to wait for it to be set up
|
||||
]).then(([roomId1, roomId2]) => {
|
||||
cy.viewRoomByName(ROOM_NAME_1);
|
||||
cy.url().should("contain", `/#/room/${roomId1}`);
|
||||
openStickerPicker();
|
||||
sendStickerFromPicker();
|
||||
expectTimelineSticker(roomId1);
|
||||
|
||||
// Ensure that when we switch to a different room that the sticker
|
||||
// goes to the right place
|
||||
cy.viewRoomByName(ROOM_NAME_2);
|
||||
cy.url().should("contain", `/#/room/${roomId2}`);
|
||||
openStickerPicker();
|
||||
sendStickerFromPicker();
|
||||
expectTimelineSticker(roomId2);
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle a sticker picker widget missing creatorUserId", () => {
|
||||
cy.createRoom({
|
||||
name: ROOM_NAME_1,
|
||||
}).as("roomId1");
|
||||
cy.setAccountData("m.widgets", {
|
||||
[STICKER_PICKER_WIDGET_ID]: {
|
||||
content: {
|
||||
type: "m.stickerpicker",
|
||||
name: STICKER_PICKER_WIDGET_NAME,
|
||||
url: stickerPickerUrl,
|
||||
// No creatorUserId
|
||||
},
|
||||
sender: userId,
|
||||
state_key: STICKER_PICKER_WIDGET_ID,
|
||||
type: "m.widget",
|
||||
id: STICKER_PICKER_WIDGET_ID,
|
||||
},
|
||||
}).as("stickers");
|
||||
|
||||
cy.all([cy.get<string>("@roomId1"), cy.get<{}>("@stickers")]).then(([roomId1]) => {
|
||||
cy.viewRoomByName(ROOM_NAME_1);
|
||||
cy.url().should("contain", `/#/room/${roomId1}`);
|
||||
openStickerPicker();
|
||||
sendStickerFromPicker();
|
||||
expectTimelineSticker(roomId1);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,207 +0,0 @@
|
|||
/*
|
||||
Copyright 2022 Mikhail Aheichyk
|
||||
Copyright 2022 Nordeck IT + Consulting GmbH.
|
||||
|
||||
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 { IWidget } from "matrix-widget-api/src/interfaces/IWidget";
|
||||
|
||||
import type { MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { HomeserverInstance } from "../../plugins/utils/homeserver";
|
||||
import { UserCredentials } from "../../support/login";
|
||||
|
||||
const DEMO_WIDGET_ID = "demo-widget-id";
|
||||
const DEMO_WIDGET_NAME = "Demo Widget";
|
||||
const DEMO_WIDGET_TYPE = "demo";
|
||||
const ROOM_NAME = "Demo";
|
||||
|
||||
const DEMO_WIDGET_HTML = `
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Demo Widget</title>
|
||||
<script>
|
||||
window.onmessage = ev => {
|
||||
if (ev.data.action === 'capabilities') {
|
||||
window.parent.postMessage(Object.assign({
|
||||
response: {
|
||||
capabilities: []
|
||||
},
|
||||
}, ev.data), '*');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<button id="demo">Demo</button>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
// mostly copied from src/utils/WidgetUtils.waitForRoomWidget with small modifications
|
||||
function waitForRoomWidget(win: Cypress.AUTWindow, widgetId: string, roomId: string, add: boolean): Promise<void> {
|
||||
const matrixClient = win.mxMatrixClientPeg.get();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
function eventsInIntendedState(evList) {
|
||||
const widgetPresent = evList.some((ev) => {
|
||||
return ev.getContent() && ev.getContent()["id"] === widgetId;
|
||||
});
|
||||
if (add) {
|
||||
return widgetPresent;
|
||||
} else {
|
||||
return !widgetPresent;
|
||||
}
|
||||
}
|
||||
|
||||
const room = matrixClient.getRoom(roomId);
|
||||
|
||||
const startingWidgetEvents = room.currentState.getStateEvents("im.vector.modular.widgets");
|
||||
if (eventsInIntendedState(startingWidgetEvents)) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
function onRoomStateEvents(ev: MatrixEvent) {
|
||||
if (ev.getRoomId() !== roomId || ev.getType() !== "im.vector.modular.widgets") return;
|
||||
|
||||
const currentWidgetEvents = room.currentState.getStateEvents("im.vector.modular.widgets");
|
||||
|
||||
if (eventsInIntendedState(currentWidgetEvents)) {
|
||||
matrixClient.removeListener(win.matrixcs.RoomStateEvent.Events, onRoomStateEvents);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
|
||||
matrixClient.on(win.matrixcs.RoomStateEvent.Events, onRoomStateEvents);
|
||||
});
|
||||
}
|
||||
|
||||
describe("Widget PIP", () => {
|
||||
let homeserver: HomeserverInstance;
|
||||
let user: UserCredentials;
|
||||
let bot: MatrixClient;
|
||||
let demoWidgetUrl: string;
|
||||
|
||||
function roomCreateAddWidgetPip(userRemove: "leave" | "kick" | "ban") {
|
||||
cy.createRoom({
|
||||
name: ROOM_NAME,
|
||||
invite: [bot.getUserId()],
|
||||
}).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");
|
||||
|
||||
// bot joins the room
|
||||
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");
|
||||
|
||||
// open the room
|
||||
cy.viewRoomByName(ROOM_NAME);
|
||||
|
||||
cy.all([
|
||||
cy.get<string>("@powerLevelsChanged"),
|
||||
cy.get<string>("@botJoined"),
|
||||
cy.get<string>("@widgetEventSent"),
|
||||
]).then(() => {
|
||||
cy.window().then(async (win) => {
|
||||
// wait for widget state event
|
||||
await waitForRoomWidget(win, DEMO_WIDGET_ID, roomId, true);
|
||||
|
||||
// activate widget in pip mode
|
||||
win.mxActiveWidgetStore.setWidgetPersistence(DEMO_WIDGET_ID, roomId, true);
|
||||
|
||||
// checks that pip window is opened
|
||||
cy.get(".mx_WidgetPip").should("exist");
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// checks that pip window is closed
|
||||
cy.get(".mx_WidgetPip").should("not.exist");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
cy.startHomeserver("default").then((data) => {
|
||||
homeserver = data;
|
||||
|
||||
cy.initTestUser(homeserver, "Mike").then((_user) => {
|
||||
user = _user;
|
||||
});
|
||||
cy.getBot(homeserver, { displayName: "Bot", autoAcceptInvites: false }).then((_bot) => {
|
||||
bot = _bot;
|
||||
});
|
||||
});
|
||||
cy.serveHtmlFile(DEMO_WIDGET_HTML).then((url) => {
|
||||
demoWidgetUrl = url;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopHomeserver(homeserver);
|
||||
cy.stopWebServers();
|
||||
});
|
||||
|
||||
it("should be closed on leave", () => {
|
||||
roomCreateAddWidgetPip("leave");
|
||||
});
|
||||
|
||||
it("should be closed on kick", () => {
|
||||
roomCreateAddWidgetPip("kick");
|
||||
});
|
||||
|
||||
it("should be closed on ban", () => {
|
||||
roomCreateAddWidgetPip("ban");
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue