Skip to content

Commit

Permalink
Merge tag 'v25.0.0' into sc
Browse files Browse the repository at this point in the history
* Change `Store.save()` to return a `Promise` ([\matrix-org#3221](matrix-org#3221)). Contributed by @texuf.
* Add typedoc-plugin-mdn-links ([\matrix-org#3292](matrix-org#3292)).
* Annotate events with executed push rule ([\matrix-org#3284](matrix-org#3284)). Contributed by @kerryarchibald.
* Element-R: pass device list change notifications into rust crypto-sdk ([\matrix-org#3254](matrix-org#3254)). Fixes element-hq/element-web#24795. Contributed by @florianduros.
* Support for MSC3882 revision 1 ([\matrix-org#3228](matrix-org#3228)). Contributed by @hughns.
* Fix screen sharing on Firefox 113 ([\matrix-org#3282](matrix-org#3282)). Contributed by @tulir.
* Retry processing potential poll events after decryption ([\matrix-org#3246](matrix-org#3246)). Fixes element-hq/element-web#24568.
* Element-R: handle events which arrive before their keys ([\matrix-org#3230](matrix-org#3230)). Fixes element-hq/element-web#24489.
  • Loading branch information
su-ex committed Apr 25, 2023
2 parents af4413b + 33bbd45 commit c0ab637
Show file tree
Hide file tree
Showing 75 changed files with 3,550 additions and 1,681 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/static_analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
run: "yarn install"

- name: Generate Docs
run: "yarn run gendoc"
run: "yarn run gendoc --treatWarningsAsErrors"

- name: Upload Artifact
uses: actions/upload-artifact@v3
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
Changes in [25.0.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v25.0.0) (2023-04-25)
==================================================================================================

## 🚨 BREAKING CHANGES
* Change `Store.save()` to return a `Promise` ([\#3221](https://github.com/matrix-org/matrix-js-sdk/pull/3221)). Contributed by @texuf.

## ✨ Features
* Add typedoc-plugin-mdn-links ([\#3292](https://github.com/matrix-org/matrix-js-sdk/pull/3292)).
* Annotate events with executed push rule ([\#3284](https://github.com/matrix-org/matrix-js-sdk/pull/3284)). Contributed by @kerryarchibald.
* Element-R: pass device list change notifications into rust crypto-sdk ([\#3254](https://github.com/matrix-org/matrix-js-sdk/pull/3254)). Fixes vector-im/element-web#24795. Contributed by @florianduros.
* Support for MSC3882 revision 1 ([\#3228](https://github.com/matrix-org/matrix-js-sdk/pull/3228)). Contributed by @hughns.

## 🐛 Bug Fixes
* Fix screen sharing on Firefox 113 ([\#3282](https://github.com/matrix-org/matrix-js-sdk/pull/3282)). Contributed by @tulir.
* Retry processing potential poll events after decryption ([\#3246](https://github.com/matrix-org/matrix-js-sdk/pull/3246)). Fixes vector-im/element-web#24568.
* Element-R: handle events which arrive before their keys ([\#3230](https://github.com/matrix-org/matrix-js-sdk/pull/3230)). Fixes vector-im/element-web#24489.

Changes in [24.1.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v24.1.0) (2023-04-11)
==================================================================================================

Expand Down
39 changes: 39 additions & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/* Copyright 2023 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.
*/

import type { Config } from "jest";
import { env } from "process";

const config: Config = {
testEnvironment: "node",
testMatch: ["<rootDir>/spec/**/*.spec.{js,ts}"],
setupFilesAfterEnv: ["<rootDir>/spec/setupTests.ts"],
collectCoverageFrom: ["<rootDir>/src/**/*.{js,ts}"],
coverageReporters: ["text-summary", "lcov"],
testResultsProcessor: "@casualbot/jest-sonar-reporter",
};

// if we're running under GHA, enable the GHA reporter
if (env["GITHUB_ACTIONS"] !== undefined) {
const reporters: Config["reporters"] = [["github-actions", { silent: false }], "summary"];

// if we're running against the develop branch, also enable the slow test reporter
if (env["GITHUB_REF"] == "refs/heads/develop") {
reporters.push("<rootDir>/spec/slowReporter.js");
}
config.reporters = reporters;
}

export default config;
29 changes: 7 additions & 22 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "matrix-js-sdk",
"version": "24.1.0",
"version": "25.0.0",
"description": "Matrix Client-Server SDK for Javascript",
"engines": {
"node": ">=16.0.0"
Expand Down Expand Up @@ -55,7 +55,7 @@
],
"dependencies": {
"@babel/runtime": "^7.12.5",
"@matrix-org/matrix-sdk-crypto-js": "^0.1.0-alpha.5",
"@matrix-org/matrix-sdk-crypto-js": "^0.1.0-alpha.6",
"another-json": "^0.2.0",
"bs58": "^5.0.0",
"content-type": "^1.0.4",
Expand Down Expand Up @@ -101,7 +101,7 @@
"debug": "^4.3.4",
"docdash": "^2.0.0",
"domexception": "^4.0.0",
"eslint": "8.35.0",
"eslint": "8.37.0",
"eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.5.1",
Expand All @@ -119,30 +119,15 @@
"jest-localstorage-mock": "^2.4.6",
"jest-mock": "^29.0.0",
"matrix-mock-request": "^2.5.0",
"prettier": "2.8.4",
"prettier": "2.8.7",
"rimraf": "^4.0.0",
"terser": "^5.5.1",
"ts-node": "^10.9.1",
"tsify": "^5.0.2",
"typedoc": "^0.23.20",
"typedoc-plugin-mdn-links": "^3.0.3",
"typedoc-plugin-missing-exports": "^1.0.0",
"typescript": "^4.5.3"
},
"jest": {
"testEnvironment": "node",
"testMatch": [
"<rootDir>/spec/**/*.spec.{js,ts}"
],
"setupFilesAfterEnv": [
"<rootDir>/spec/setupTests.ts"
],
"collectCoverageFrom": [
"<rootDir>/src/**/*.{js,ts}"
],
"coverageReporters": [
"text-summary",
"lcov"
],
"testResultsProcessor": "@casualbot/jest-sonar-reporter"
"typescript": "^5.0.0"
},
"@casualbot/jest-sonar-reporter": {
"outputDirectory": "coverage",
Expand Down
70 changes: 32 additions & 38 deletions spec/integ/crypto.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -624,10 +624,8 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
expect(decryptedEvent.getContent().body).toEqual("42");
});

oldBackendOnly("Alice receives a megolm message before the session keys", async () => {
it("Alice receives a megolm message before the session keys", async () => {
expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} });

// https://github.com/vector-im/element-web/issues/2273
await startClientAndAwaitFirstSync();

// if we're using the old crypto impl, stub out some methods in the device manager.
Expand Down Expand Up @@ -667,7 +665,11 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
await syncPromise(aliceClient);

const room = aliceClient.getRoom(ROOM_ID)!;
expect(room.getLiveTimeline().getEvents()[0].getContent().msgtype).toEqual("m.bad.encrypted");
const event = room.getLiveTimeline().getEvents()[0];

// wait for a first attempt at decryption: should fail
await testUtils.awaitDecryption(event);
expect(event.getContent().msgtype).toEqual("m.bad.encrypted");

// now she gets the room_key event
syncResponder.sendOrQueueSyncResponse({
Expand All @@ -678,20 +680,8 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
});
await syncPromise(aliceClient);

const event = room.getLiveTimeline().getEvents()[0];

let decryptedEvent: MatrixEvent;
if (event.getContent().msgtype != "m.bad.encrypted") {
decryptedEvent = event;
} else {
decryptedEvent = await new Promise<MatrixEvent>((resolve) => {
event.once(MatrixEventEvent.Decrypted, (ev) => {
logger.log(`${Date.now()} event ${event.getId()} now decrypted`);
resolve(ev);
});
});
}
expect(decryptedEvent.getContent().body).toEqual("42");
await testUtils.awaitDecryption(event, { waitOnDecryptionFailure: true });
expect(event.getContent().body).toEqual("42");
});

it("Alice gets a second room_key message", async () => {
Expand Down Expand Up @@ -1947,51 +1937,51 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
jest.useRealTimers();
});

function listenToUpload(): Promise<number> {
function awaitKeyUploadRequest(): Promise<{ keysCount: number; fallbackKeysCount: number }> {
return new Promise((resolve) => {
const listener = (url: string, options: RequestInit) => {
const content = JSON.parse(options.body as string);
const keysCount = Object.keys(content?.one_time_keys || {}).length;
if (keysCount) resolve(keysCount);
const fallbackKeysCount = Object.keys(content?.fallback_keys || {}).length;
if (keysCount) resolve({ keysCount, fallbackKeysCount });
return {
one_time_key_counts: {
// The matrix client does `/upload` requests until 50 keys are uploaded
// We return here 60 to avoid the `/upload` request loop
signed_curve25519: keysCount ? 60 : keysCount,
},
};
};

// catch both r0 and v3 variants
fetchMock.post(
new URL("/_matrix/client/r0/keys/upload", aliceClient.getHomeserverUrl()).toString(),
listener,
{
for (const path of ["/_matrix/client/r0/keys/upload", "/_matrix/client/v3/keys/upload"]) {
fetchMock.post(new URL(path, aliceClient.getHomeserverUrl()).toString(), listener, {
// These routes are already defined in the E2EKeyReceiver
// We want to overwrite the behaviour of the E2EKeyReceiver
overwriteRoutes: true,
},
);
fetchMock.post(
new URL("/_matrix/client/v3/keys/upload", aliceClient.getHomeserverUrl()).toString(),
listener,
{
overwriteRoutes: true,
},
);
});
}
});
}

it("should make key upload request after sync", async () => {
let uploadPromise = listenToUpload();
let uploadPromise = awaitKeyUploadRequest();
expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} });
await startClientAndAwaitFirstSync();

syncResponder.sendOrQueueSyncResponse(getSyncResponse([]));

await syncPromise(aliceClient);
expect(await uploadPromise).toBeGreaterThan(0);

uploadPromise = listenToUpload();
// Verify that `/upload` is called on Alice's homesever
const { keysCount, fallbackKeysCount } = await uploadPromise;
expect(keysCount).toBeGreaterThan(0);
expect(fallbackKeysCount).toBe(0);

uploadPromise = awaitKeyUploadRequest();
syncResponder.sendOrQueueSyncResponse({
next_batch: 2,
device_one_time_keys_count: { signed_curve25519: 0 },
device_unused_fallback_key_types: [],
});

// Advance local date to 2 minutes
Expand All @@ -2000,7 +1990,11 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,

await syncPromise(aliceClient);

expect(await uploadPromise).toBeGreaterThan(0);
// After we set device_one_time_keys_count to 0
// a `/upload` is expected
const res = await uploadPromise;
expect(res.keysCount).toBeGreaterThan(0);
expect(res.fallbackKeysCount).toBeGreaterThan(0);
});
});
});
35 changes: 32 additions & 3 deletions spec/integ/matrix-client-methods.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1127,22 +1127,51 @@ describe("MatrixClient", function () {

describe("requestLoginToken", () => {
it("should hit the expected API endpoint with UIA", async () => {
httpBackend!
.when("GET", "/capabilities")
.respond(200, { capabilities: { "org.matrix.msc3882.get_login_token": { enabled: true } } });
const response = {};
const uiaData = {};
const prom = client!.requestLoginToken(uiaData);
httpBackend!
.when("POST", "/unstable/org.matrix.msc3882/login/token", { auth: uiaData })
.when("POST", "/unstable/org.matrix.msc3882/login/get_token", { auth: uiaData })
.respond(200, response);
await httpBackend!.flush("");
expect(await prom).toStrictEqual(response);
});

it("should hit the expected API endpoint without UIA", async () => {
const response = {};
httpBackend!
.when("GET", "/capabilities")
.respond(200, { capabilities: { "org.matrix.msc3882.get_login_token": { enabled: true } } });
const response = { login_token: "xyz", expires_in_ms: 5000 };
const prom = client!.requestLoginToken();
httpBackend!.when("POST", "/unstable/org.matrix.msc3882/login/get_token", {}).respond(200, response);
await httpBackend!.flush("");
// check that expires_in has been populated for compatibility with r0
expect(await prom).toStrictEqual({ ...response, expires_in: 5 });
});

it("should hit the r1 endpoint when capability is disabled", async () => {
httpBackend!
.when("GET", "/capabilities")
.respond(200, { capabilities: { "org.matrix.msc3882.get_login_token": { enabled: false } } });
const response = { login_token: "xyz", expires_in_ms: 5000 };
const prom = client!.requestLoginToken();
httpBackend!.when("POST", "/unstable/org.matrix.msc3882/login/get_token", {}).respond(200, response);
await httpBackend!.flush("");
// check that expires_in has been populated for compatibility with r0
expect(await prom).toStrictEqual({ ...response, expires_in: 5 });
});

it("should hit the r0 endpoint for fallback", async () => {
httpBackend!.when("GET", "/capabilities").respond(200, {});
const response = { login_token: "xyz", expires_in: 5 };
const prom = client!.requestLoginToken();
httpBackend!.when("POST", "/unstable/org.matrix.msc3882/login/token", {}).respond(200, response);
await httpBackend!.flush("");
expect(await prom).toStrictEqual(response);
// check that expires_in has been populated for compatibility with r1
expect(await prom).toStrictEqual({ ...response, expires_in_ms: 5000 });
});
});

Expand Down
31 changes: 10 additions & 21 deletions spec/integ/sliding-sync-sdk.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -662,41 +662,30 @@ describe("SlidingSyncSdk", () => {
});

it("can update device lists", () => {
client!.crypto!.processDeviceLists = jest.fn();
ext.onResponse({
device_lists: {
changed: ["@alice:localhost"],
left: ["@bob:localhost"],
},
});
// TODO: more assertions?
expect(client!.crypto!.processDeviceLists).toHaveBeenCalledWith({
changed: ["@alice:localhost"],
left: ["@bob:localhost"],
});
});

it("can update OTK counts", () => {
client!.crypto!.updateOneTimeKeyCount = jest.fn();
it("can update OTK counts and unused fallback keys", () => {
client!.crypto!.processKeyCounts = jest.fn();
ext.onResponse({
device_one_time_keys_count: {
signed_curve25519: 42,
},
});
expect(client!.crypto!.updateOneTimeKeyCount).toHaveBeenCalledWith(42);
ext.onResponse({
device_one_time_keys_count: {
not_signed_curve25519: 42,
// missing field -> default to 0
},
});
expect(client!.crypto!.updateOneTimeKeyCount).toHaveBeenCalledWith(0);
});

it("can update fallback keys", () => {
ext.onResponse({
device_unused_fallback_key_types: ["signed_curve25519"],
});
expect(client!.crypto!.getNeedsNewFallback()).toEqual(false);
ext.onResponse({
device_unused_fallback_key_types: ["not_signed_curve25519"],
});
expect(client!.crypto!.getNeedsNewFallback()).toEqual(true);
expect(client!.crypto!.processKeyCounts).toHaveBeenCalledWith({ signed_curve25519: 42 }, [
"signed_curve25519",
]);
});
});

Expand Down
2 changes: 1 addition & 1 deletion spec/setupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ limitations under the License.

import DOMException from "domexception";

global.DOMException = DOMException;
global.DOMException = DOMException as typeof global.DOMException;

jest.mock("../src/http-api/utils", () => ({
...jest.requireActual("../src/http-api/utils"),
Expand Down
Loading

0 comments on commit c0ab637

Please sign in to comment.