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
|
# 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)
|
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.
|
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
|
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.
|
[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:
|
To start the client:
|
||||||
|
|
||||||
```javascript
|
```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
|
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:
|
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 the generation of "friendly" room and member names.
|
||||||
- Handles historical `RoomMember` information (e.g. display names).
|
- Handles historical `RoomMember` information (e.g. display names).
|
||||||
- Manages room member state across multiple events (e.g. it handles typing, power
|
- 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 room initial sync on accepting invites.
|
||||||
- Handles WebRTC calling.
|
- 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
|
# 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
|
You can also use the sdk with [Deno](https://deno.land/) (`import npm:matrix-js-sdk`) but its not officialy supported.
|
||||||
emits object models (e.g. `Rooms`, `RoomMembers`) when they
|
|
||||||
are updated.
|
## 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
|
```javascript
|
||||||
// Listen for low-level MatrixEvents
|
// Listen for low-level MatrixEvents
|
||||||
@ -161,40 +149,16 @@ client.on(RoomMemberEvent.Typing, function (event, member) {
|
|||||||
client.startClient();
|
client.startClient();
|
||||||
```
|
```
|
||||||
|
|
||||||
### Promises and Callbacks
|
## Entry points
|
||||||
|
|
||||||
Most of the methods in the SDK are asynchronous: they do not directly return a
|
As well as the primary entry point (`matrix-js-sdk`), there are several other entry points which may be useful:
|
||||||
result, but instead return a [Promise](http://documentup.com/kriskowal/q/)
|
|
||||||
which will be fulfilled in the future.
|
|
||||||
|
|
||||||
The typical usage is something like:
|
| Entry point | Description |
|
||||||
|
| ------------------------------ | --------------------------------------------------------------------------------------------------- |
|
||||||
```javascript
|
| `matrix-js-sdk` | Primary entry point. High-level functionality, and lots of historical clutter in need of a cleanup. |
|
||||||
matrixClient.someMethod(arg1, arg2).then(function(result) {
|
| `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. |
|
||||||
```
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
@ -306,6 +270,9 @@ Then visit `http://localhost:8005` to see the API docs.
|
|||||||
|
|
||||||
# End-to-end encryption support
|
# 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
|
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
|
[libolm](https://gitlab.matrix.org/matrix-org/olm). It is left up to the
|
||||||
application to make libolm available, via the `Olm` global.
|
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",
|
"$schema": "https://typedoc.org/schema.json",
|
||||||
"plugin": ["typedoc-plugin-mdn-links", "typedoc-plugin-missing-exports", "typedoc-plugin-coverage"],
|
"plugin": ["typedoc-plugin-mdn-links", "typedoc-plugin-missing-exports", "typedoc-plugin-coverage"],
|
||||||
"coverageLabel": "TypeDoc",
|
"coverageLabel": "TypeDoc",
|
||||||
"entryPoints": ["src/matrix.ts", "src/types.ts"],
|
"entryPoints": ["src/matrix.ts", "src/types.ts", "src/testing.ts"],
|
||||||
"excludeExternals": true,
|
"excludeExternals": true,
|
||||||
"out": "_docs"
|
"out": "_docs"
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user