Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 7919c69

Browse files
committed
Merge branch 'develop' into staging
2 parents e3692ce + 29c1932 commit 7919c69

File tree

803 files changed

+9016
-6569
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

803 files changed

+9016
-6569
lines changed

.eslintrc.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,12 @@ module.exports = {
100100
files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}", "cypress/**/*.ts"],
101101
extends: ["plugin:matrix-org/typescript", "plugin:matrix-org/react"],
102102
rules: {
103-
// temporary disabled
104-
"@typescript-eslint/explicit-function-return-type": "off",
103+
"@typescript-eslint/explicit-function-return-type": [
104+
"error",
105+
{
106+
allowExpressions: true,
107+
},
108+
],
105109

106110
// Things we do that break the ideal style
107111
"prefer-promise-reject-errors": "off",

.github/workflows/cypress.yaml

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,16 @@ jobs:
8484
actions: read
8585
issues: read
8686
pull-requests: read
87-
environment:
88-
Cypress
89-
#strategy:
90-
# fail-fast: false
91-
# matrix:
92-
# # Run 4 instances in Parallel
93-
# runner: [1, 2, 3, 4]
87+
environment: Cypress
88+
strategy:
89+
fail-fast: false
90+
matrix:
91+
# Run 4 instances in Parallel
92+
runner: [1, 2, 3, 4]
9493
steps:
94+
- uses: browser-actions/setup-chrome@latest
95+
- run: echo "BROWSER_PATH=$(which chrome)" >> $GITHUB_ENV
96+
9597
- uses: tecolicom/actions-use-apt-tools@v1
9698
with:
9799
# Our test suite includes some screenshot tests with unusual diacritics, which are
@@ -121,14 +123,12 @@ jobs:
121123
with:
122124
# The built-in Electron runner seems to grind to a halt trying
123125
# to run the tests, so use chrome.
124-
browser: chrome
126+
browser: "${{ env.BROWSER_PATH }}"
125127
start: npx serve -p 8080 webapp
126128
wait-on: "http://localhost:8080"
127-
record:
128-
true
129-
#parallel: true
130-
#command-prefix: 'yarn percy exec --parallel --'
131-
command-prefix: "yarn percy exec --"
129+
record: true
130+
parallel: true
131+
command-prefix: "yarn percy exec --parallel --"
132132
config: '{"reporter":"cypress-multi-reporters", "reporterOptions": { "configFile": "cypress-ci-reporter-config.json" } }'
133133
ci-build-id: ${{ needs.prepare.outputs.uuid }}
134134
env:
@@ -151,6 +151,8 @@ jobs:
151151
COMMIT_INFO_MESSAGE: ${{ needs.prepare.outputs.commit_message }}
152152
COMMIT_INFO_AUTHOR: ${{ needs.prepare.outputs.commit_author }}
153153
COMMIT_INFO_EMAIL: ${{ needs.prepare.outputs.commit_email }}
154+
CYPRESS_PULL_REQUEST_ID: ${{ needs.prepare.outputs.pr_id }}
155+
CYPRESS_PULL_REQUEST_URL: https://github.com/${{ github.repository }}/pull/${{ needs.prepare.outputs.pr_id }}
154156

155157
# pass the Percy token as an environment variable
156158
PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}
@@ -159,9 +161,8 @@ jobs:
159161
# tell Percy more details about the context of this run
160162
PERCY_BRANCH: ${{ github.event.workflow_run.head_branch }}
161163
PERCY_COMMIT: ${{ github.event.workflow_run.head_sha }}
162-
PERCY_PULL_REQUEST:
163-
${{ needs.prepare.outputs.pr_id }}
164-
#PERCY_PARALLEL_TOTAL: ${{ strategy.job-total }}
164+
PERCY_PULL_REQUEST: ${{ needs.prepare.outputs.pr_id }}
165+
PERCY_PARALLEL_TOTAL: ${{ strategy.job-total }}
165166
PERCY_PARALLEL_NONCE: ${{ needs.prepare.outputs.uuid }}
166167

167168
- name: Upload Artifact

.github/workflows/i18n_check.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
- name: "Get modified files"
1313
id: changed_files
1414
if: github.event_name == 'pull_request' && github.event.pull_request.user.login != 'RiotTranslateBot'
15-
uses: tj-actions/changed-files@v34
15+
uses: tj-actions/changed-files@v35
1616
with:
1717
files: |
1818
src/i18n/strings/*

cypress.config.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,11 @@ export default defineConfig({
2727
return require("./cypress/plugins/index.ts").default(on, config);
2828
},
2929
baseUrl: "http://localhost:8080",
30-
experimentalSessionAndOrigin: true,
3130
specPattern: "cypress/e2e/**/*.{js,jsx,ts,tsx}",
3231
},
3332
env: {
34-
// Docker tag to use for `ghcr.io/matrix-org/sliding-sync-proxy` image.
35-
SLIDING_SYNC_PROXY_TAG: "v0.6.0",
33+
// Docker tag to use for `ghcr.io/matrix-org/sliding-sync` image.
34+
SLIDING_SYNC_PROXY_TAG: "v0.99.0-rc1",
3635
HOMESERVER: "synapse",
3736
},
3837
retries: {

cypress/e2e/composer/composer.spec.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,27 @@ describe("Composer", () => {
120120

121121
// Type another
122122
cy.get("div[contenteditable=true]").type("my message 1");
123-
// Press enter. Would be nice to just use {enter} but we can't because Cypress
124-
// does not trigger an insertParagraph when you do that.
125-
cy.get("div[contenteditable=true]").trigger("input", { inputType: "insertParagraph" });
123+
// Send message
124+
cy.get("div[contenteditable=true]").type("{enter}");
126125
// It was sent
127126
cy.contains(".mx_EventTile_body", "my message 1");
128127
});
129128

129+
it("sends only one message when you press Enter multiple times", () => {
130+
// Type a message
131+
cy.get("div[contenteditable=true]").type("my message 0");
132+
// It has not been sent yet
133+
cy.contains(".mx_EventTile_body", "my message 0").should("not.exist");
134+
135+
// Click send
136+
cy.get("div[contenteditable=true]").type("{enter}");
137+
cy.get("div[contenteditable=true]").type("{enter}");
138+
cy.get("div[contenteditable=true]").type("{enter}");
139+
// It has been sent
140+
cy.contains(".mx_EventTile_body", "my message 0");
141+
cy.get(".mx_EventTile_body").should("have.length", 1);
142+
});
143+
130144
it("can write formatted text", () => {
131145
cy.get("div[contenteditable=true]").type("my {ctrl+b}bold{ctrl+b} message");
132146
cy.get('div[aria-label="Send message"]').click();
@@ -141,7 +155,7 @@ describe("Composer", () => {
141155
it("only sends when you press Ctrl+Enter", () => {
142156
// Type a message and press Enter
143157
cy.get("div[contenteditable=true]").type("my message 3");
144-
cy.get("div[contenteditable=true]").trigger("input", { inputType: "insertParagraph" });
158+
cy.get("div[contenteditable=true]").type("{enter}");
145159
// It has not been sent yet
146160
cy.contains(".mx_EventTile_body", "my message 3").should("not.exist");
147161

cypress/e2e/crypto/crypto.spec.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import type { ISasEvent } from "matrix-js-sdk/src/crypto/verification/SAS";
2020
import type { CypressBot } from "../../support/bot";
2121
import { HomeserverInstance } from "../../plugins/utils/homeserver";
2222
import Chainable = Cypress.Chainable;
23+
import { UserCredentials } from "../../support/login";
2324

2425
type EmojiMapping = [emoji: string, name: string];
2526
interface CryptoTestContext extends Mocha.Context {
@@ -154,11 +155,15 @@ const verify = function (this: CryptoTestContext) {
154155
};
155156

156157
describe("Cryptography", function () {
158+
let aliceCredentials: UserCredentials;
159+
157160
beforeEach(function () {
158161
cy.startHomeserver("default")
159162
.as("homeserver")
160163
.then((homeserver: HomeserverInstance) => {
161-
cy.initTestUser(homeserver, "Alice", undefined, "alice_");
164+
cy.initTestUser(homeserver, "Alice", undefined, "alice_").then((credentials) => {
165+
aliceCredentials = credentials;
166+
});
162167
cy.getBot(homeserver, { displayName: "Bob", autoAcceptInvites: false, userIdPrefix: "bob_" }).as("bob");
163168
});
164169
});
@@ -183,7 +188,7 @@ describe("Cryptography", function () {
183188
});
184189

185190
it("creating a DM should work, being e2e-encrypted / user verification", function (this: CryptoTestContext) {
186-
cy.bootstrapCrossSigning();
191+
cy.bootstrapCrossSigning(aliceCredentials);
187192
startDMWithBob.call(this);
188193
// send first message
189194
cy.get(".mx_BasicMessageComposer_input").click().should("have.focus").type("Hey!{enter}");
@@ -194,7 +199,7 @@ describe("Cryptography", function () {
194199
});
195200

196201
it("should allow verification when there is no existing DM", function (this: CryptoTestContext) {
197-
cy.bootstrapCrossSigning();
202+
cy.bootstrapCrossSigning(aliceCredentials);
198203
autoJoin(this.bob);
199204

200205
// we need to have a room with the other user present, so we can open the verification panel
@@ -212,7 +217,7 @@ describe("Cryptography", function () {
212217
});
213218

214219
it("should show the correct shield on edited e2e events", function (this: CryptoTestContext) {
215-
cy.bootstrapCrossSigning();
220+
cy.bootstrapCrossSigning(aliceCredentials);
216221

217222
// bob has a second, not cross-signed, device
218223
cy.loginBot(this.homeserver, this.bob.getUserId(), this.bob.__cypress_password, {}).as("bobSecondDevice");

cypress/e2e/crypto/decryption-failure.spec.ts

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,9 @@ describe("Decryption Failure Bar", () => {
105105
"and there are other verified devices or backups",
106106
() => {
107107
let otherDevice: MatrixClient | undefined;
108-
cy.loginBot(homeserver, testUser.username, testUser.password, {})
108+
cy.loginBot(homeserver, testUser.username, testUser.password, { bootstrapCrossSigning: true })
109109
.then(async (cli) => {
110110
otherDevice = cli;
111-
await otherDevice.bootstrapCrossSigning({
112-
authUploadDeviceSigningKeys: async (makeRequest) => {
113-
await makeRequest({});
114-
},
115-
setupNewCrossSigning: true,
116-
});
117111
})
118112
.then(() => {
119113
cy.botSendMessage(bot, roomId, "test");
@@ -169,15 +163,11 @@ describe("Decryption Failure Bar", () => {
169163
"should prompt the user to reset keys, if this device isn't verified " +
170164
"and there are no other verified devices or backups",
171165
() => {
172-
cy.loginBot(homeserver, testUser.username, testUser.password, {}).then(async (cli) => {
173-
await cli.bootstrapCrossSigning({
174-
authUploadDeviceSigningKeys: async (makeRequest) => {
175-
await makeRequest({});
176-
},
177-
setupNewCrossSigning: true,
178-
});
179-
await cli.logout(true);
180-
});
166+
cy.loginBot(homeserver, testUser.username, testUser.password, { bootstrapCrossSigning: true }).then(
167+
async (cli) => {
168+
await cli.logout(true);
169+
},
170+
);
181171

182172
cy.botSendMessage(bot, roomId, "test");
183173
cy.wait(5000);

cypress/e2e/editing/editing.spec.ts

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ limitations under the License.
1616

1717
/// <reference types="cypress" />
1818

19-
import type { MsgType } from "matrix-js-sdk/src/@types/event";
19+
import type { EventType, MsgType } from "matrix-js-sdk/src/@types/event";
2020
import type { ISendEventResponse } from "matrix-js-sdk/src/@types/requests";
21-
import type { EventType } from "matrix-js-sdk/src/@types/event";
21+
import type { IContent } from "matrix-js-sdk/src/models/event";
2222
import { HomeserverInstance } from "../../plugins/utils/homeserver";
2323
import Chainable = Cypress.Chainable;
2424

@@ -29,6 +29,16 @@ const sendEvent = (roomId: string): Chainable<ISendEventResponse> => {
2929
});
3030
};
3131

32+
/** generate a message event which will take up some room on the page. */
33+
function mkPadding(n: number): IContent {
34+
return {
35+
msgtype: "m.text" as MsgType,
36+
body: `padding ${n}`,
37+
format: "org.matrix.custom.html",
38+
formatted_body: `<h3>Test event ${n}</h3>\n`.repeat(10),
39+
};
40+
}
41+
3242
describe("Editing", () => {
3343
let homeserver: HomeserverInstance;
3444

@@ -37,7 +47,6 @@ describe("Editing", () => {
3747
homeserver = data;
3848
cy.initTestUser(homeserver, "Edith").then(() => {
3949
cy.injectAxe();
40-
return cy.createRoom({ name: "Test room" }).as("roomId");
4150
});
4251
});
4352
});
@@ -47,6 +56,8 @@ describe("Editing", () => {
4756
});
4857

4958
it("should close the composer when clicking save after making a change and undoing it", () => {
59+
cy.createRoom({ name: "Test room" }).as("roomId");
60+
5061
cy.get<string>("@roomId").then((roomId) => {
5162
sendEvent(roomId);
5263
cy.visit("/#/room/" + roomId);
@@ -64,4 +75,77 @@ describe("Editing", () => {
6475
// Assert that the edit composer has gone away
6576
cy.get(".mx_EditMessageComposer").should("not.exist");
6677
});
78+
79+
it("should correctly display events which are edited, where we lack the edit event", () => {
80+
// This tests the behaviour when a message has been edited some time after it has been sent, and we
81+
// jump back in room history to view the event, but do not have the actual edit event.
82+
//
83+
// In that scenario, we rely on the server to replace the content (pre-MSC3925), or do it ourselves based on
84+
// the bundled edit event (post-MSC3925).
85+
//
86+
// To test it, we need to have a room with lots of events in, so we can jump around the timeline without
87+
// paginating in the event itself. Hence, we create a bot user which creates the room and populates it before
88+
// we join.
89+
90+
let testRoomId: string;
91+
let originalEventId: string;
92+
let editEventId: string;
93+
94+
// create a second user
95+
const bobChainable = cy.getBot(homeserver, { displayName: "Bob", userIdPrefix: "bob_" });
96+
97+
cy.all([cy.window({ log: false }), bobChainable]).then(async ([win, bob]) => {
98+
// "bob" now creates the room, and sends a load of events in it. Note that all of this happens via calls on
99+
// the js-sdk rather than Cypress commands, so uses regular async/await.
100+
101+
const room = await bob.createRoom({ name: "TestRoom", visibility: win.matrixcs.Visibility.Public });
102+
testRoomId = room.room_id;
103+
cy.log(`Bot user created room ${room.room_id}`);
104+
105+
originalEventId = (await bob.sendMessage(room.room_id, { body: "original", msgtype: "m.text" })).event_id;
106+
cy.log(`Bot user sent original event ${originalEventId}`);
107+
108+
// send a load of padding events. We make them large, so that they fill the whole screen
109+
// and the client doesn't end up paginating into the event we want.
110+
let i = 0;
111+
while (i < 10) {
112+
await bob.sendMessage(room.room_id, mkPadding(i++));
113+
}
114+
115+
// ... then the edit ...
116+
editEventId = (
117+
await bob.sendMessage(room.room_id, {
118+
"m.new_content": { body: "Edited body", msgtype: "m.text" },
119+
"m.relates_to": {
120+
rel_type: "m.replace",
121+
event_id: originalEventId,
122+
},
123+
"body": "* edited",
124+
"msgtype": "m.text",
125+
})
126+
).event_id;
127+
cy.log(`Bot user sent edit event ${editEventId}`);
128+
129+
// ... then a load more padding ...
130+
while (i < 20) {
131+
await bob.sendMessage(room.room_id, mkPadding(i++));
132+
}
133+
});
134+
135+
cy.getClient().then((cli) => {
136+
// now have the cypress user join the room, jump to the original event, and wait for the event to be
137+
// visible
138+
cy.joinRoom(testRoomId);
139+
cy.viewRoomByName("TestRoom");
140+
cy.visit(`#/room/${testRoomId}/${originalEventId}`);
141+
cy.get(`[data-event-id="${originalEventId}"]`).should((messageTile) => {
142+
// at this point, the edit event should still be unknown
143+
expect(cli.getRoom(testRoomId).getTimelineForEvent(editEventId)).to.be.null;
144+
145+
// nevertheless, the event should be updated
146+
expect(messageTile.find(".mx_EventTile_body").text()).to.eq("Edited body");
147+
expect(messageTile.find(".mx_EventTile_edited")).to.exist;
148+
});
149+
});
150+
});
67151
});

cypress/e2e/integration-manager/kick.spec.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,27 @@ function sendActionFromIntegrationManager(integrationManagerUrl: string, targetR
8282
});
8383
}
8484

85+
function clickUntilGone(selector: string, attempt = 0) {
86+
if (attempt === 11) {
87+
throw new Error("clickUntilGone attempt count exceeded");
88+
}
89+
90+
cy.get(selector)
91+
.last()
92+
.click()
93+
.then(($button) => {
94+
const exists = Cypress.$(selector).length > 0;
95+
if (exists) {
96+
clickUntilGone(selector, ++attempt);
97+
}
98+
});
99+
}
100+
85101
function expectKickedMessage(shouldExist: boolean) {
86-
// Expand any event summaries
87-
cy.get(".mx_GenericEventListSummary_toggle[aria-expanded=false]").click({ multiple: true });
102+
// Expand any event summaries, we can't use a click multiple here because clicking one might de-render others
103+
// This is quite horrible but seems the most stable way of clicking 0-N buttons,
104+
// one at a time with a full re-evaluation after each click
105+
clickUntilGone(".mx_GenericEventListSummary_toggle[aria-expanded=false]");
88106

89107
// Check for the event message (or lack thereof)
90108
cy.contains(".mx_EventTile_line", `${USER_DISPLAY_NAME} removed ${BOT_DISPLAY_NAME}: ${KICK_REASON}`).should(

0 commit comments

Comments
 (0)