Convert end-to-end tests to Typescript (#7206)

This commit is contained in:
James Salter 2021-12-06 09:59:06 +11:00 committed by GitHub
parent 5219b6be80
commit d4813f7a1a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 653 additions and 441 deletions

View file

@ -15,11 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
const request = require('request-promise-native');
const cheerio = require('cheerio');
const url = require("url");
import request = require('request-promise-native');
import * as cheerio from 'cheerio';
import * as url from "url";
module.exports.approveConsent = async function(consentUrl) {
export const approveConsent = async function(consentUrl: string): Promise<void> {
const body = await request.get(consentUrl);
const doc = cheerio.load(body);
const v = doc("input[name=v]").val();

View file

@ -15,12 +15,17 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
const { exec } = require('child_process');
const request = require('request-promise-native');
const RestSession = require('./session');
const RestMultiSession = require('./multi');
import { exec } from 'child_process';
import request = require('request-promise-native');
import { RestSession } from './session';
import { RestMultiSession } from './multi';
function execAsync(command, options) {
interface ExecResult {
stdout: string;
stderr: string;
}
function execAsync(command: string, options: Parameters<typeof exec>[1]): Promise<ExecResult> {
return new Promise((resolve, reject) => {
exec(command, options, (error, stdout, stderr) => {
if (error) {
@ -32,27 +37,32 @@ function execAsync(command, options) {
});
}
module.exports = class RestSessionCreator {
constructor(synapseSubdir, hsUrl, cwd) {
this.synapseSubdir = synapseSubdir;
this.hsUrl = hsUrl;
this.cwd = cwd;
}
export interface Credentials {
accessToken: string;
homeServer: string;
userId: string;
deviceId: string;
hsUrl: string;
}
async createSessionRange(usernames, password, groupName) {
export class RestSessionCreator {
constructor(private readonly synapseSubdir: string, private readonly hsUrl: string, private readonly cwd: string) {}
public async createSessionRange(usernames: string[], password: string,
groupName: string): Promise<RestMultiSession> {
const sessionPromises = usernames.map((username) => this.createSession(username, password));
const sessions = await Promise.all(sessionPromises);
return new RestMultiSession(sessions, groupName);
}
async createSession(username, password) {
await this._register(username, password);
public async createSession(username: string, password: string): Promise<RestSession> {
await this.register(username, password);
console.log(` * created REST user ${username} ... done`);
const authResult = await this._authenticate(username, password);
const authResult = await this.authenticate(username, password);
return new RestSession(authResult);
}
async _register(username, password) {
private async register(username: string, password: string): Promise<void> {
const registerArgs = [
'-c homeserver.yaml',
`-u ${username}`,
@ -70,7 +80,7 @@ module.exports = class RestSessionCreator {
await execAsync(allCmds, { cwd: this.cwd, encoding: 'utf-8' });
}
async _authenticate(username, password) {
private async authenticate(username: string, password: string): Promise<Credentials> {
const requestBody = {
"type": "m.login.password",
"identifier": {
@ -89,4 +99,4 @@ module.exports = class RestSessionCreator {
hsUrl: this.hsUrl,
};
}
};
}

View file

@ -15,19 +15,22 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
const Logger = require('../logger');
import { Logger } from '../logger';
import { RestSession } from "./session";
import { RestRoom } from "./room";
module.exports = class RestMultiSession {
constructor(sessions, groupName) {
export class RestMultiSession {
readonly log: Logger;
constructor(public readonly sessions: RestSession[], groupName: string) {
this.log = new Logger(groupName);
this.sessions = sessions;
}
slice(groupName, start, end) {
public slice(groupName: string, start: number, end?: number): RestMultiSession {
return new RestMultiSession(this.sessions.slice(start, end), groupName);
}
pop(userName) {
public pop(userName: string): RestSession {
const idx = this.sessions.findIndex((s) => s.userName() === userName);
if (idx === -1) {
throw new Error(`user ${userName} not found`);
@ -36,9 +39,9 @@ module.exports = class RestMultiSession {
return session;
}
async setDisplayName(fn) {
public async setDisplayName(fn: (s: RestSession) => string): Promise<void> {
this.log.step("set their display name");
await Promise.all(this.sessions.map(async (s) => {
await Promise.all(this.sessions.map(async (s: RestSession) => {
s.log.mute();
await s.setDisplayName(fn(s));
s.log.unmute();
@ -46,7 +49,7 @@ module.exports = class RestMultiSession {
this.log.done();
}
async join(roomIdOrAlias) {
public async join(roomIdOrAlias: string): Promise<RestMultiRoom> {
this.log.step(`join ${roomIdOrAlias}`);
const rooms = await Promise.all(this.sessions.map(async (s) => {
s.log.mute();
@ -58,22 +61,19 @@ module.exports = class RestMultiSession {
return new RestMultiRoom(rooms, roomIdOrAlias, this.log);
}
room(roomIdOrAlias) {
public room(roomIdOrAlias: string): RestMultiRoom {
const rooms = this.sessions.map(s => s.room(roomIdOrAlias));
return new RestMultiRoom(rooms, roomIdOrAlias, this.log);
}
};
}
class RestMultiRoom {
constructor(rooms, roomIdOrAlias, log) {
this.rooms = rooms;
this.roomIdOrAlias = roomIdOrAlias;
this.log = log;
}
constructor(public readonly rooms: RestRoom[], private readonly roomIdOrAlias: string,
private readonly log: Logger) {}
async talk(message) {
public async talk(message: string): Promise<void> {
this.log.step(`say "${message}" in ${this.roomIdOrAlias}`);
await Promise.all(this.rooms.map(async (r) => {
await Promise.all(this.rooms.map(async (r: RestRoom) => {
r.log.mute();
await r.talk(message);
r.log.unmute();
@ -81,7 +81,7 @@ class RestMultiRoom {
this.log.done();
}
async leave() {
public async leave() {
this.log.step(`leave ${this.roomIdOrAlias}`);
await Promise.all(this.rooms.map(async (r) => {
r.log.mute();

View file

@ -15,20 +15,18 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
const uuidv4 = require('uuid/v4');
import uuidv4 = require('uuid/v4');
import { RestSession } from "./session";
import { Logger } from "../logger";
/* no pun intented */
module.exports = class RestRoom {
constructor(session, roomId, log) {
this.session = session;
this._roomId = roomId;
this.log = log;
}
export class RestRoom {
constructor(readonly session: RestSession, readonly roomId: string, readonly log: Logger) {}
async talk(message) {
this.log.step(`says "${message}" in ${this._roomId}`);
async talk(message: string): Promise<void> {
this.log.step(`says "${message}" in ${this.roomId}`);
const txId = uuidv4();
await this.session._put(`/rooms/${this._roomId}/send/m.room.message/${txId}`, {
await this.session.put(`/rooms/${this.roomId}/send/m.room.message/${txId}`, {
"msgtype": "m.text",
"body": message,
});
@ -36,13 +34,9 @@ module.exports = class RestRoom {
return txId;
}
async leave() {
this.log.step(`leaves ${this._roomId}`);
await this.session._post(`/rooms/${this._roomId}/leave`);
async leave(): Promise<void> {
this.log.step(`leaves ${this.roomId}`);
await this.session.post(`/rooms/${this.roomId}/leave`);
this.log.done();
}
roomId() {
return this._roomId;
}
};
}

View file

@ -1,126 +0,0 @@
/*
Copyright 2018 New Vector Ltd
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.
*/
const request = require('request-promise-native');
const Logger = require('../logger');
const RestRoom = require('./room');
const { approveConsent } = require('./consent');
module.exports = class RestSession {
constructor(credentials) {
this.log = new Logger(credentials.userId);
this._credentials = credentials;
this._displayName = null;
this._rooms = {};
}
userId() {
return this._credentials.userId;
}
userName() {
return this._credentials.userId.split(":")[0].substr(1);
}
displayName() {
return this._displayName;
}
async setDisplayName(displayName) {
this.log.step(`sets their display name to ${displayName}`);
this._displayName = displayName;
await this._put(`/profile/${this._credentials.userId}/displayname`, {
displayname: displayName,
});
this.log.done();
}
async join(roomIdOrAlias) {
this.log.step(`joins ${roomIdOrAlias}`);
const roomId = (await this._post(`/join/${encodeURIComponent(roomIdOrAlias)}`)).room_id;
this.log.done();
const room = new RestRoom(this, roomId, this.log);
this._rooms[roomId] = room;
this._rooms[roomIdOrAlias] = room;
return room;
}
room(roomIdOrAlias) {
if (this._rooms.hasOwnProperty(roomIdOrAlias)) {
return this._rooms[roomIdOrAlias];
} else {
throw new Error(`${this._credentials.userId} is not in ${roomIdOrAlias}`);
}
}
async createRoom(name, options) {
this.log.step(`creates room ${name}`);
const body = {
name,
};
if (options.invite) {
body.invite = options.invite;
}
if (options.public) {
body.visibility = "public";
} else {
body.visibility = "private";
}
if (options.dm) {
body.is_direct = true;
}
if (options.topic) {
body.topic = options.topic;
}
const roomId = (await this._post(`/createRoom`, body)).room_id;
this.log.done();
return new RestRoom(this, roomId, this.log);
}
_post(csApiPath, body) {
return this._request("POST", csApiPath, body);
}
_put(csApiPath, body) {
return this._request("PUT", csApiPath, body);
}
async _request(method, csApiPath, body) {
try {
const responseBody = await request({
url: `${this._credentials.hsUrl}/_matrix/client/r0${csApiPath}`,
method,
headers: {
"Authorization": `Bearer ${this._credentials.accessToken}`,
},
json: true,
body,
});
return responseBody;
} catch (err) {
const responseBody = err.response.body;
if (responseBody.errcode === 'M_CONSENT_NOT_GIVEN') {
await approveConsent(responseBody.consent_uri);
return this._request(method, csApiPath, body);
} else if (responseBody && responseBody.error) {
throw new Error(`${method} ${csApiPath}: ${responseBody.error}`);
} else {
throw new Error(`${method} ${csApiPath}: ${err.response.statusCode}`);
}
}
}
};

View file

@ -0,0 +1,137 @@
/*
Copyright 2018 New Vector Ltd
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 request = require('request-promise-native');
import { Logger } from '../logger';
import { RestRoom } from './room';
import { approveConsent } from './consent';
import { Credentials } from "./creator";
interface RoomOptions {
invite: string;
public: boolean;
topic: string;
dm: boolean;
}
export class RestSession {
private _displayName: string = null;
private readonly rooms: Record<string, RestRoom> = {};
readonly log: Logger;
constructor(private readonly credentials: Credentials) {
this.log = new Logger(credentials.userId);
}
userId(): string {
return this.credentials.userId;
}
userName(): string {
return this.credentials.userId.split(":")[0].substr(1);
}
displayName(): string {
return this._displayName;
}
async setDisplayName(displayName: string): Promise<void> {
this.log.step(`sets their display name to ${displayName}`);
this._displayName = displayName;
await this.put(`/profile/${this.credentials.userId}/displayname`, {
displayname: displayName,
});
this.log.done();
}
async join(roomIdOrAlias: string): Promise<RestRoom> {
this.log.step(`joins ${roomIdOrAlias}`);
const roomId = (await this.post(`/join/${encodeURIComponent(roomIdOrAlias)}`)).room_id;
this.log.done();
const room = new RestRoom(this, roomId, this.log);
this.rooms[roomId] = room;
this.rooms[roomIdOrAlias] = room;
return room;
}
room(roomIdOrAlias: string): RestRoom {
if (this.rooms.hasOwnProperty(roomIdOrAlias)) {
return this.rooms[roomIdOrAlias];
} else {
throw new Error(`${this.credentials.userId} is not in ${roomIdOrAlias}`);
}
}
async createRoom(name: string, options: RoomOptions): Promise<RestRoom> {
this.log.step(`creates room ${name}`);
const body = {
name,
};
if (options.invite) {
body['invite'] = options.invite;
}
if (options.public) {
body['visibility'] = "public";
} else {
body['visibility'] = "private";
}
if (options.dm) {
body['is_direct'] = true;
}
if (options.topic) {
body['topic'] = options.topic;
}
const roomId = (await this.post(`/createRoom`, body)).room_id;
this.log.done();
return new RestRoom(this, roomId, this.log);
}
post(csApiPath: string, body?: any): Promise<any> {
return this.request("POST", csApiPath, body);
}
put(csApiPath: string, body?: any): Promise<any> {
return this.request("PUT", csApiPath, body);
}
async request(method: string, csApiPath: string, body?: any): Promise<any> {
try {
return await request({
url: `${this.credentials.hsUrl}/_matrix/client/r0${csApiPath}`,
method,
headers: {
"Authorization": `Bearer ${this.credentials.accessToken}`,
},
json: true,
body,
});
} catch (err) {
if (!err.response) {
throw err;
}
const responseBody = err.response.body;
if (responseBody.errcode === 'M_CONSENT_NOT_GIVEN') {
await approveConsent(responseBody.consent_uri);
return this.request(method, csApiPath, body);
} else if (responseBody && responseBody.error) {
throw new Error(`${method} ${csApiPath}: ${responseBody.error}`);
} else {
throw new Error(`${method} ${csApiPath}: ${err.response.statusCode}`);
}
}
}
}