You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-07-05 00:22:28 +03:00
Add some test utils in a new entrypoint (#4127)
* Clean up README a little This just removes some of the most egregious lies and outdated stuff. There's a *lot* more that can be done here. * Add some test utils in a new entrypoint * Fix comment * Update src/testing.ts
This commit is contained in:
committed by
GitHub
parent
4ba1341f8f
commit
dce8acbf17
77
README.md
77
README.md
@ -21,16 +21,6 @@ endpoints from before Matrix 1.1, for example.
|
||||
|
||||
# Quickstart
|
||||
|
||||
## In a browser
|
||||
|
||||
### Note, the browserify build has been removed. Please use a bundler like webpack or vite instead.
|
||||
|
||||
## In Node.js
|
||||
|
||||
Ensure you have the latest LTS version of Node.js installed.
|
||||
This library relies on `fetch` which is available in Node from v18.0.0 - it should work fine also with polyfills.
|
||||
If you wish to use a ponyfill or adapter of some sort then pass it as `fetchFn` to the MatrixClient constructor options.
|
||||
|
||||
Using `yarn` instead of `npm` is recommended. Please see the Yarn [install guide](https://classic.yarnpkg.com/en/docs/install)
|
||||
if you do not have it already.
|
||||
|
||||
@ -47,8 +37,6 @@ client.publicRooms(function (err, data) {
|
||||
See below for how to include libolm to enable end-to-end-encryption. Please check
|
||||
[the Node.js terminal app](examples/node) for a more complex example.
|
||||
|
||||
You can also use the sdk with [Deno](https://deno.land/) (`import npm:matrix-js-sdk`) but its not officialy supported.
|
||||
|
||||
To start the client:
|
||||
|
||||
```javascript
|
||||
@ -106,7 +94,7 @@ Object.keys(client.store.rooms).forEach((roomId) => {
|
||||
This SDK provides a full object model around the Matrix Client-Server API and emits
|
||||
events for incoming data and state changes. Aside from wrapping the HTTP API, it:
|
||||
|
||||
- Handles syncing (via `/initialSync` and `/events`)
|
||||
- Handles syncing (via `/sync`)
|
||||
- Handles the generation of "friendly" room and member names.
|
||||
- Handles historical `RoomMember` information (e.g. display names).
|
||||
- Manages room member state across multiple events (e.g. it handles typing, power
|
||||
@ -127,20 +115,20 @@ events for incoming data and state changes. Aside from wrapping the HTTP API, it
|
||||
- Handles room initial sync on accepting invites.
|
||||
- Handles WebRTC calling.
|
||||
|
||||
Later versions of the SDK will:
|
||||
|
||||
- Expose a `RoomSummary` which would be suitable for a recents page.
|
||||
- Provide different pluggable storage layers (e.g. local storage, database-backed)
|
||||
|
||||
# Usage
|
||||
|
||||
## Conventions
|
||||
## Supported platforms
|
||||
|
||||
### Emitted events
|
||||
`matrix-js-sdk` can be used in either Node.js applications (ensure you have the latest LTS version of Node.js installed),
|
||||
or in browser applications, via a bundler such as Webpack or Vite.
|
||||
|
||||
The SDK will emit events using an `EventEmitter`. It also
|
||||
emits object models (e.g. `Rooms`, `RoomMembers`) when they
|
||||
are updated.
|
||||
You can also use the sdk with [Deno](https://deno.land/) (`import npm:matrix-js-sdk`) but its not officialy supported.
|
||||
|
||||
## Emitted events
|
||||
|
||||
The SDK raises notifications to the application using
|
||||
[`EventEmitter`s](https://nodejs.org/api/events.html#class-eventemitter). The `MatrixClient` itself
|
||||
implements `EventEmitter`, as do many of the high-level abstractions such as `Room` and `RoomMember`.
|
||||
|
||||
```javascript
|
||||
// Listen for low-level MatrixEvents
|
||||
@ -161,40 +149,16 @@ client.on(RoomMemberEvent.Typing, function (event, member) {
|
||||
client.startClient();
|
||||
```
|
||||
|
||||
### Promises and Callbacks
|
||||
## Entry points
|
||||
|
||||
Most of the methods in the SDK are asynchronous: they do not directly return a
|
||||
result, but instead return a [Promise](http://documentup.com/kriskowal/q/)
|
||||
which will be fulfilled in the future.
|
||||
As well as the primary entry point (`matrix-js-sdk`), there are several other entry points which may be useful:
|
||||
|
||||
The typical usage is something like:
|
||||
|
||||
```javascript
|
||||
matrixClient.someMethod(arg1, arg2).then(function(result) {
|
||||
...
|
||||
});
|
||||
```
|
||||
|
||||
Alternatively, if you have a Node.js-style `callback(err, result)` function,
|
||||
you can pass the result of the promise into it with something like:
|
||||
|
||||
```javascript
|
||||
matrixClient.someMethod(arg1, arg2).nodeify(callback);
|
||||
```
|
||||
|
||||
The main thing to note is that it is problematic to discard the result of a
|
||||
promise-returning function, as that will cause exceptions to go unobserved.
|
||||
|
||||
Methods which return a promise show this in their documentation.
|
||||
|
||||
Many methods in the SDK support _both_ Node.js-style callbacks _and_ Promises,
|
||||
via an optional `callback` argument. The callback support is now deprecated:
|
||||
new methods do not include a `callback` argument, and in the future it may be
|
||||
removed from existing methods.
|
||||
|
||||
## Low level types
|
||||
|
||||
There are some low level TypeScript types exported via the `matrix-js-sdk/lib/types` entrypoint to not bloat the main entrypoint.
|
||||
| Entry point | Description |
|
||||
| ------------------------------ | --------------------------------------------------------------------------------------------------- |
|
||||
| `matrix-js-sdk` | Primary entry point. High-level functionality, and lots of historical clutter in need of a cleanup. |
|
||||
| `matrix-js-sdk/lib/crypto-api` | Cryptography functionality. |
|
||||
| `matrix-js-sdk/lib/types` | Low-level types, reflecting data structures defined in the Matrix spec. |
|
||||
| `matrix-js-sdk/lib/testing` | Test utilities, which may be useful in test code but should not be used in production code. |
|
||||
|
||||
## Examples
|
||||
|
||||
@ -306,6 +270,9 @@ Then visit `http://localhost:8005` to see the API docs.
|
||||
|
||||
# End-to-end encryption support
|
||||
|
||||
**This section is outdated.** Use of `libolm` is deprecated and we are replacing it with support
|
||||
from the matrix-rust-sdk (https://github.com/element-hq/element-web/issues/21972).
|
||||
|
||||
The SDK supports end-to-end encryption via the Olm and Megolm protocols, using
|
||||
[libolm](https://gitlab.matrix.org/matrix-org/olm). It is left up to the
|
||||
application to make libolm available, via the `Olm` global.
|
||||
|
88
spec/unit/testing.spec.ts
Normal file
88
spec/unit/testing.spec.ts
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright 2024 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 { mkDecryptionFailureMatrixEvent, mkEncryptedMatrixEvent, mkMatrixEvent } from "../../src/testing";
|
||||
import { EventType } from "../../src";
|
||||
|
||||
describe("testing", () => {
|
||||
describe("mkMatrixEvent", () => {
|
||||
it("makes an event", () => {
|
||||
const event = mkMatrixEvent({
|
||||
content: { body: "blah" },
|
||||
sender: "@alice:test",
|
||||
type: EventType.RoomMessage,
|
||||
roomId: "!test:room",
|
||||
});
|
||||
|
||||
expect(event.getContent()).toEqual({ body: "blah" });
|
||||
expect(event.sender?.userId).toEqual("@alice:test");
|
||||
expect(event.isState()).toBe(false);
|
||||
});
|
||||
|
||||
it("makes a state event", () => {
|
||||
const event = mkMatrixEvent({
|
||||
content: { body: "blah" },
|
||||
sender: "@alice:test",
|
||||
type: EventType.RoomTopic,
|
||||
roomId: "!test:room",
|
||||
stateKey: "",
|
||||
});
|
||||
|
||||
expect(event.getContent()).toEqual({ body: "blah" });
|
||||
expect(event.sender?.userId).toEqual("@alice:test");
|
||||
expect(event.isState()).toBe(true);
|
||||
expect(event.getStateKey()).toEqual("");
|
||||
});
|
||||
});
|
||||
|
||||
describe("mkEncryptedMatrixEvent", () => {
|
||||
it("makes an event", async () => {
|
||||
const event = await mkEncryptedMatrixEvent({
|
||||
plainContent: { body: "blah" },
|
||||
sender: "@alice:test",
|
||||
plainType: EventType.RoomMessage,
|
||||
roomId: "!test:room",
|
||||
});
|
||||
|
||||
expect(event.sender?.userId).toEqual("@alice:test");
|
||||
expect(event.isEncrypted()).toBe(true);
|
||||
expect(event.isDecryptionFailure()).toBe(false);
|
||||
expect(event.getContent()).toEqual({ body: "blah" });
|
||||
expect(event.getType()).toEqual("m.room.message");
|
||||
});
|
||||
});
|
||||
|
||||
describe("mkDecryptionFailureMatrixEvent", () => {
|
||||
it("makes an event", async () => {
|
||||
const event = await mkDecryptionFailureMatrixEvent({
|
||||
sender: "@alice:test",
|
||||
roomId: "!test:room",
|
||||
code: "UNKNOWN",
|
||||
msg: "blah",
|
||||
});
|
||||
|
||||
expect(event.sender?.userId).toEqual("@alice:test");
|
||||
expect(event.isEncrypted()).toBe(true);
|
||||
expect(event.isDecryptionFailure()).toBe(true);
|
||||
expect(event.getContent()).toEqual({
|
||||
body: "** Unable to decrypt: DecryptionError: blah **",
|
||||
msgtype: "m.bad.encrypted",
|
||||
});
|
||||
expect(event.getType()).toEqual("m.room.message");
|
||||
expect(event.isState()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
165
src/testing.ts
Normal file
165
src/testing.ts
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
Copyright 2024 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file is a secondary entrypoint for the js-sdk library, exposing utilities which might be useful for writing tests.
|
||||
*
|
||||
* In general it should not be included in runtime applications.
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
import { IContent, IEvent, IUnsigned, MatrixEvent } from "./models/event";
|
||||
import { RoomMember } from "./models/room-member";
|
||||
import { EventType } from "./@types/event";
|
||||
import { IEventDecryptionResult } from "./@types/crypto";
|
||||
import { DecryptionError } from "./crypto/algorithms";
|
||||
|
||||
/**
|
||||
* Create a {@link MatrixEvent}.
|
||||
*
|
||||
* @param opts - Values for the event.
|
||||
*/
|
||||
export function mkMatrixEvent(opts: {
|
||||
/** Room ID of the event. */
|
||||
roomId: string;
|
||||
|
||||
/** The sender of the event. */
|
||||
sender: string;
|
||||
|
||||
/** The type of the event. */
|
||||
type: EventType | string;
|
||||
|
||||
/** Optional `state_key` for the event. If unspecified, a non-state event is created. */
|
||||
stateKey?: string;
|
||||
|
||||
/** Optional `origin_server_ts` for the event. If unspecified, the timestamp will be set to 0. */
|
||||
ts?: number;
|
||||
|
||||
/** Optional `event_id` for the event. If provided will be used as event ID; else an ID is generated. */
|
||||
eventId?: string;
|
||||
|
||||
/** Content of the event. */
|
||||
content: IContent;
|
||||
|
||||
/** Optional `unsigned` data for the event. */
|
||||
unsigned?: IUnsigned;
|
||||
}): MatrixEvent {
|
||||
const event: Partial<IEvent> = {
|
||||
type: opts.type,
|
||||
room_id: opts.roomId,
|
||||
sender: opts.sender,
|
||||
content: opts.content,
|
||||
event_id: opts.eventId ?? "$" + Math.random() + "-" + Math.random(),
|
||||
origin_server_ts: opts.ts ?? 0,
|
||||
unsigned: opts.unsigned,
|
||||
};
|
||||
if (opts.stateKey !== undefined) {
|
||||
event.state_key = opts.stateKey;
|
||||
}
|
||||
|
||||
const mxEvent = new MatrixEvent(event);
|
||||
mxEvent.sender = {
|
||||
userId: opts.sender,
|
||||
membership: "join",
|
||||
name: opts.sender,
|
||||
rawDisplayName: opts.sender,
|
||||
roomId: opts.sender,
|
||||
getAvatarUrl: () => {},
|
||||
getMxcAvatarUrl: () => {},
|
||||
} as unknown as RoomMember;
|
||||
return mxEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a `MatrixEvent` representing a successfully-decrypted `m.room.encrypted` event.
|
||||
*
|
||||
* @param opts - Values for the event.
|
||||
*/
|
||||
export async function mkEncryptedMatrixEvent(opts: {
|
||||
/** Room ID of the event. */
|
||||
roomId: string;
|
||||
|
||||
/** The sender of the event. */
|
||||
sender: string;
|
||||
|
||||
/** The type the event will have, once it has been decrypted. */
|
||||
plainType: EventType | string;
|
||||
|
||||
/** The content the event will have, once it has been decrypted. */
|
||||
plainContent: IContent;
|
||||
}): Promise<MatrixEvent> {
|
||||
// we construct an event which has been decrypted by stubbing out CryptoBackend.decryptEvent and then
|
||||
// calling MatrixEvent.attemptDecryption.
|
||||
|
||||
const mxEvent = mkMatrixEvent({
|
||||
type: EventType.RoomMessageEncrypted,
|
||||
roomId: opts.roomId,
|
||||
sender: opts.sender,
|
||||
content: { algorithm: "m.megolm.v1.aes-sha2" },
|
||||
});
|
||||
|
||||
const decryptionResult: IEventDecryptionResult = {
|
||||
claimedEd25519Key: "",
|
||||
clearEvent: {
|
||||
type: opts.plainType,
|
||||
content: opts.plainContent,
|
||||
},
|
||||
forwardingCurve25519KeyChain: [],
|
||||
senderCurve25519Key: "",
|
||||
untrusted: false,
|
||||
};
|
||||
|
||||
const mockCrypto = {
|
||||
decryptEvent: async (_ev): Promise<IEventDecryptionResult> => decryptionResult,
|
||||
} as Parameters<MatrixEvent["attemptDecryption"]>[0];
|
||||
await mxEvent.attemptDecryption(mockCrypto);
|
||||
return mxEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a `MatrixEvent` representing a `m.room.encrypted` event which could not be decrypted.
|
||||
*
|
||||
* @param opts - Values for the event.
|
||||
*/
|
||||
export async function mkDecryptionFailureMatrixEvent(opts: {
|
||||
/** Room ID of the event. */
|
||||
roomId: string;
|
||||
|
||||
/** The sender of the event. */
|
||||
sender: string;
|
||||
|
||||
/** The reason code for the failure */
|
||||
code: string;
|
||||
|
||||
/** A textual reason for the failure */
|
||||
msg: string;
|
||||
}): Promise<MatrixEvent> {
|
||||
const mxEvent = mkMatrixEvent({
|
||||
type: EventType.RoomMessageEncrypted,
|
||||
roomId: opts.roomId,
|
||||
sender: opts.sender,
|
||||
content: { algorithm: "m.megolm.v1.aes-sha2" },
|
||||
});
|
||||
|
||||
const mockCrypto = {
|
||||
decryptEvent: async (_ev): Promise<IEventDecryptionResult> => {
|
||||
throw new DecryptionError(opts.code, opts.msg);
|
||||
},
|
||||
} as Parameters<MatrixEvent["attemptDecryption"]>[0];
|
||||
await mxEvent.attemptDecryption(mockCrypto);
|
||||
return mxEvent;
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
"$schema": "https://typedoc.org/schema.json",
|
||||
"plugin": ["typedoc-plugin-mdn-links", "typedoc-plugin-missing-exports", "typedoc-plugin-coverage"],
|
||||
"coverageLabel": "TypeDoc",
|
||||
"entryPoints": ["src/matrix.ts", "src/types.ts"],
|
||||
"entryPoints": ["src/matrix.ts", "src/types.ts", "src/testing.ts"],
|
||||
"excludeExternals": true,
|
||||
"out": "_docs"
|
||||
}
|
||||
|
Reference in New Issue
Block a user