Skip to content

Commit dee577f

Browse files
committed
Merge branch 'rav/element-r/14.0_new_verifier_base_methods' into rav/element-r/14_verifier_interface
2 parents 4d2a062 + 727cb37 commit dee577f

17 files changed

Lines changed: 457 additions & 86 deletions

.github/workflows/cypress.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Triggers after the "Downstream artifacts" build has finished, to run the
2+
# cypress tests (with access to repo secrets)
3+
4+
name: matrix-react-sdk Cypress End to End Tests
5+
on:
6+
workflow_run:
7+
workflows: ["Build downstream artifacts"]
8+
types:
9+
- completed
10+
11+
concurrency:
12+
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch || github.run_id }}
13+
cancel-in-progress: ${{ github.event.workflow_run.event == 'pull_request' }}
14+
15+
jobs:
16+
cypress:
17+
name: Cypress
18+
uses: matrix-org/matrix-react-sdk/.github/workflows/cypress.yaml@develop
19+
permissions:
20+
actions: read
21+
issues: read
22+
statuses: write
23+
pull-requests: read
24+
secrets:
25+
# secrets are not automatically shared with called workflows, so share the cypress dashboard key
26+
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
27+
with:
28+
react-sdk-repository: matrix-org/matrix-react-sdk
29+
rust-crypto: true
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Build downstream artifacts
2+
on:
3+
pull_request: {}
4+
merge_group:
5+
types: [checks_requested]
6+
7+
# For now at least, we don't run this or the cypress-tests against pushes
8+
# to develop or master.
9+
#
10+
# Note that if we later choose to do so, we'll need to find a way to stop
11+
# the results in Cypress Cloud from clobbering those from the 'develop'
12+
# branch of matrix-react-sdk.
13+
#
14+
#push:
15+
# branches: [develop, master]
16+
concurrency:
17+
group: ${{ github.workflow }}-${{ github.ref }}
18+
cancel-in-progress: true
19+
jobs:
20+
build-element-web:
21+
name: Build element-web
22+
uses: matrix-org/matrix-react-sdk/.github/workflows/element-web.yaml@develop
23+
with:
24+
matrix-js-sdk-sha: ${{ github.sha }}
25+
react-sdk-repository: matrix-org/matrix-react-sdk

jest.config.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,19 @@ const config: Config = {
2323
collectCoverageFrom: ["<rootDir>/src/**/*.{js,ts}"],
2424
coverageReporters: ["text-summary", "lcov"],
2525
testResultsProcessor: "@casualbot/jest-sonar-reporter",
26+
27+
// Always print out a summary if there are any failing tests. Normally
28+
// a summary is only printed if there are more than 20 test *suites*.
29+
reporters: [["default", { summaryThreshold: 0 }]],
2630
};
2731

2832
// if we're running under GHA, enable the GHA reporter
2933
if (env["GITHUB_ACTIONS"] !== undefined) {
30-
const reporters: Config["reporters"] = [["github-actions", { silent: false }], "summary"];
34+
const reporters: Config["reporters"] = [
35+
["github-actions", { silent: false }],
36+
// as above: always show a summary if there were any failing tests.
37+
["summary", { summaryThreshold: 0 }],
38+
];
3139

3240
// if we're running against the develop branch, also enable the slow test reporter
3341
if (env["GITHUB_REF"] == "refs/heads/develop") {

spec/integ/crypto/verification.spec.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -245,10 +245,6 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st
245245
oldBackendOnly(
246246
"Outgoing verification: can verify another device via QR code with an untrusted cross-signing key",
247247
async () => {
248-
// we need to have bootstrapped cross-signing for this
249-
//await bootstrapCrossSigning(aliceClient);
250-
// console.warn("Bootstrapped");
251-
252248
// expect requests to download our own keys
253249
fetchMock.post(new RegExp("/_matrix/client/(r0|v3)/keys/query"), {
254250
device_keys: {
@@ -304,6 +300,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st
304300
expect(qrCodeBuffer.readUint8(7)).toEqual(0x02); // mode
305301
const txnIdLen = qrCodeBuffer.readUint16BE(8);
306302
expect(qrCodeBuffer.subarray(10, 10 + txnIdLen).toString("utf-8")).toEqual(transactionId);
303+
// Alice's device's public key comes next, but we have nothing to do with it here.
307304
// const aliceDevicePubKey = qrCodeBuffer.subarray(10 + txnIdLen, 32 + 10 + txnIdLen);
308305
expect(qrCodeBuffer.subarray(42 + txnIdLen, 32 + 42 + txnIdLen)).toEqual(
309306
Buffer.from(MASTER_CROSS_SIGNING_PUBLIC_KEY_BASE64, "base64"),
@@ -327,18 +324,13 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st
327324
// there should now be a verifier
328325
const verifier: Verifier = request.verifier!;
329326
expect(verifier).toBeDefined();
330-
expect(verifier.getReciprocateQrCodeCallbacks()).toBeNull();
331327

332328
// ... which we call .verify on, which emits a ShowReciprocateQr event
333329
const verificationPromise = verifier.verify();
334330
const reciprocateQRCodeCallbacks = await new Promise<ShowQrCodeCallbacks>((resolve) => {
335331
verifier.once(VerifierEvent.ShowReciprocateQr, resolve);
336332
});
337333

338-
// getReciprocateQrCodeCallbacks() is an alternative way to get the callbacks
339-
expect(verifier.getReciprocateQrCodeCallbacks()).toBe(reciprocateQRCodeCallbacks);
340-
expect(verifier.getShowSasCallbacks()).toBeNull();
341-
342334
// Alice confirms she is happy
343335
reciprocateQRCodeCallbacks.confirm();
344336

spec/integ/matrix-client-event-timeline.spec.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,6 +1274,7 @@ describe("MatrixClient event timelines", function () {
12741274
THREAD_ROOT.event_id,
12751275
THREAD_REPLY.event_id,
12761276
THREAD_REPLY2.getId(),
1277+
THREAD_ROOT_REACTION.getId(),
12771278
THREAD_REPLY3.getId(),
12781279
]);
12791280
});
@@ -1322,7 +1323,7 @@ describe("MatrixClient event timelines", function () {
13221323
request.respond(200, function () {
13231324
return {
13241325
original_event: root,
1325-
chunk: [replies],
1326+
chunk: replies,
13261327
// no next batch as this is the oldest end of the timeline
13271328
};
13281329
});
@@ -1479,7 +1480,7 @@ describe("MatrixClient event timelines", function () {
14791480
user: userId,
14801481
type: "m.room.message",
14811482
content: {
1482-
"body": "thread reply",
1483+
"body": "thread2 reply",
14831484
"msgtype": "m.text",
14841485
"m.relates_to": {
14851486
// We can't use the const here because we change server support mode for test
@@ -1499,7 +1500,7 @@ describe("MatrixClient event timelines", function () {
14991500
user: userId,
15001501
type: "m.room.message",
15011502
content: {
1502-
"body": "thread reply",
1503+
"body": "thread reply2",
15031504
"msgtype": "m.text",
15041505
"m.relates_to": {
15051506
// We can't use the const here because we change server support mode for test
@@ -1567,7 +1568,7 @@ describe("MatrixClient event timelines", function () {
15671568
// Test adding a second event to the first thread
15681569
const thread = room.getThread(THREAD_ROOT.event_id!)!;
15691570
thread.initialEventsFetched = true;
1570-
const prom = emitPromise(room, ThreadEvent.NewReply);
1571+
const prom = emitPromise(room, ThreadEvent.Update);
15711572
respondToEvent(THREAD_ROOT_UPDATED);
15721573
respondToEvent(THREAD_ROOT_UPDATED);
15731574
respondToEvent(THREAD_ROOT_UPDATED);

spec/integ/sliding-sync-sdk.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { fail } from "assert";
2020

2121
import { SlidingSync, SlidingSyncEvent, MSC3575RoomData, SlidingSyncState, Extension } from "../../src/sliding-sync";
2222
import { TestClient } from "../TestClient";
23-
import { IRoomEvent, IStateEvent } from "../../src/sync-accumulator";
23+
import { IRoomEvent, IStateEvent } from "../../src";
2424
import {
2525
MatrixClient,
2626
MatrixEvent,
@@ -39,7 +39,7 @@ import {
3939
} from "../../src";
4040
import { SlidingSyncSdk } from "../../src/sliding-sync-sdk";
4141
import { SyncApiOptions, SyncState } from "../../src/sync";
42-
import { IStoredClientOpts } from "../../src/client";
42+
import { IStoredClientOpts } from "../../src";
4343
import { logger } from "../../src/logger";
4444
import { emitPromise } from "../test-utils/test-utils";
4545
import { defer } from "../../src/utils";

spec/unit/event-timeline-set.spec.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,6 @@ describe("EventTimelineSet", () => {
142142
});
143143

144144
describe("addEventToTimeline", () => {
145-
let thread: Thread;
146-
147-
beforeEach(() => {
148-
(client.supportsThreads as jest.Mock).mockReturnValue(true);
149-
thread = new Thread("!thread_id:server", messageEvent, { room, client });
150-
});
151-
152145
it("Adds event to timeline", () => {
153146
const liveTimeline = eventTimelineSet.getLiveTimeline();
154147
expect(liveTimeline.getEvents().length).toStrictEqual(0);
@@ -167,6 +160,15 @@ describe("EventTimelineSet", () => {
167160
eventTimelineSet.addEventToTimeline(messageEvent, liveTimeline, true, false);
168161
}).not.toThrow();
169162
});
163+
});
164+
165+
describe("addEventToTimeline (thread timeline)", () => {
166+
let thread: Thread;
167+
168+
beforeEach(() => {
169+
(client.supportsThreads as jest.Mock).mockReturnValue(true);
170+
thread = new Thread("!thread_id:server", messageEvent, { room, client });
171+
});
170172

171173
it("should not add an event to a timeline that does not belong to the timelineSet", () => {
172174
const eventTimelineSet2 = new EventTimelineSet(room);
@@ -197,7 +199,14 @@ describe("EventTimelineSet", () => {
197199
const liveTimeline = eventTimelineSetForThread.getLiveTimeline();
198200
expect(liveTimeline.getEvents().length).toStrictEqual(0);
199201

200-
eventTimelineSetForThread.addEventToTimeline(messageEvent, liveTimeline, {
202+
const normalMessage = utils.mkMessage({
203+
room: roomId,
204+
user: userA,
205+
msg: "Hello!",
206+
event: true,
207+
});
208+
209+
eventTimelineSetForThread.addEventToTimeline(normalMessage, liveTimeline, {
201210
toStartOfTimeline: true,
202211
});
203212
expect(liveTimeline.getEvents().length).toStrictEqual(0);
@@ -336,7 +345,9 @@ describe("EventTimelineSet", () => {
336345
});
337346

338347
it("should return true if the timeline set is not for a thread and the event is a thread root", () => {
348+
const thread = new Thread(messageEvent.getId()!, messageEvent, { room, client });
339349
const eventTimelineSet = new EventTimelineSet(room, {}, client);
350+
messageEvent.setThread(thread);
340351
expect(eventTimelineSet.canContain(messageEvent)).toBeTruthy();
341352
});
342353

spec/unit/login.spec.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
1+
import fetchMock from "fetch-mock-jest";
2+
3+
import { ClientPrefix, MatrixClient } from "../../src";
14
import { SSOAction } from "../../src/@types/auth";
25
import { TestClient } from "../TestClient";
36

7+
function createExampleMatrixClient(): MatrixClient {
8+
return new MatrixClient({
9+
baseUrl: "https://example.com",
10+
});
11+
}
12+
413
describe("Login request", function () {
514
let client: TestClient;
615

@@ -57,3 +66,84 @@ describe("SSO login URL", function () {
5766
});
5867
});
5968
});
69+
70+
describe("refreshToken", () => {
71+
afterEach(() => {
72+
fetchMock.mockReset();
73+
});
74+
75+
it("requests the correctly-prefixed /refresh endpoint when server correctly accepts /v3", async () => {
76+
const client = createExampleMatrixClient();
77+
78+
const response = {
79+
access_token: "access_token",
80+
refresh_token: "refresh_token",
81+
expires_in_ms: 30000,
82+
};
83+
84+
fetchMock.postOnce(client.http.getUrl("/refresh", undefined, ClientPrefix.V3).toString(), response);
85+
fetchMock.postOnce(client.http.getUrl("/refresh", undefined, ClientPrefix.V1).toString(), () => {
86+
throw new Error("/v1/refresh unexpectedly called");
87+
});
88+
89+
const refreshResult = await client.refreshToken("initial_refresh_token");
90+
expect(refreshResult).toEqual(response);
91+
});
92+
93+
it("falls back to /v1 when server does not recognized /v3 refresh", async () => {
94+
const client = createExampleMatrixClient();
95+
96+
const response = {
97+
access_token: "access_token",
98+
refresh_token: "refresh_token",
99+
expires_in_ms: 30000,
100+
};
101+
102+
fetchMock.postOnce(client.http.getUrl("/refresh", undefined, ClientPrefix.V3).toString(), {
103+
status: 400,
104+
body: { errcode: "M_UNRECOGNIZED" },
105+
});
106+
fetchMock.postOnce(client.http.getUrl("/refresh", undefined, ClientPrefix.V1).toString(), response);
107+
108+
const refreshResult = await client.refreshToken("initial_refresh_token");
109+
expect(refreshResult).toEqual(response);
110+
});
111+
112+
it("re-raises M_UNRECOGNIZED exceptions from /v1", async () => {
113+
const client = createExampleMatrixClient();
114+
115+
fetchMock.postOnce(client.http.getUrl("/refresh", undefined, ClientPrefix.V3).toString(), {
116+
status: 400,
117+
body: { errcode: "M_UNRECOGNIZED" },
118+
});
119+
fetchMock.postOnce(client.http.getUrl("/refresh", undefined, ClientPrefix.V1).toString(), {
120+
status: 400,
121+
body: { errcode: "M_UNRECOGNIZED" },
122+
});
123+
124+
expect(client.refreshToken("initial_refresh_token")).rejects.toMatchObject({ errcode: "M_UNRECOGNIZED" });
125+
});
126+
127+
it("re-raises non-M_UNRECOGNIZED exceptions from /v3", async () => {
128+
const client = createExampleMatrixClient();
129+
130+
fetchMock.postOnce(client.http.getUrl("/refresh", undefined, ClientPrefix.V3).toString(), 429);
131+
fetchMock.postOnce(client.http.getUrl("/refresh", undefined, ClientPrefix.V1).toString(), () => {
132+
throw new Error("/v1/refresh unexpectedly called");
133+
});
134+
135+
expect(client.refreshToken("initial_refresh_token")).rejects.toMatchObject({ httpStatus: 429 });
136+
});
137+
138+
it("re-raises non-M_UNRECOGNIZED exceptions from /v1", async () => {
139+
const client = createExampleMatrixClient();
140+
141+
fetchMock.postOnce(client.http.getUrl("/refresh", undefined, ClientPrefix.V3).toString(), {
142+
status: 400,
143+
body: { errcode: "M_UNRECOGNIZED" },
144+
});
145+
fetchMock.postOnce(client.http.getUrl("/refresh", undefined, ClientPrefix.V1).toString(), 429);
146+
147+
expect(client.refreshToken("initial_refresh_token")).rejects.toMatchObject({ httpStatus: 429 });
148+
});
149+
});

0 commit comments

Comments
 (0)