test: switch from mocha to jest

This commit is contained in:
Jonas Gloning 2023-02-14 13:39:46 +01:00
parent 476299ed08
commit 33a312d21c
No known key found for this signature in database
GPG Key ID: 684639B5E59E7614
17 changed files with 2592 additions and 1241 deletions

View File

@ -29,7 +29,7 @@ jobs:
- run: npm ci
- run: npm run build
- run: npm run lint
- run: npm run coverage:lcov
- run: npm run coverage
- name: Publish code coverage to CodeClimate
uses: paambaati/codeclimate-action@v3.2.0
env:

View File

@ -1,6 +0,0 @@
{
"$schema": "https://json.schemastore.org/mocharc",
"require": "source-map-support/register",
"require": "ts-node/register",
"spec": "test/**/*.ts"
}

View File

@ -1,4 +1,5 @@
import { expect } from 'chai';
import { describe, expect, it } from "@jest/globals";
import { Client } from '../../../../src/models/client';
import { HeartbeatHandler } from '../../../../src/messageHandler/handlers';
@ -11,6 +12,6 @@ describe('Heartbeat handler', () => {
HeartbeatHandler(client);
expect(client.getLastPing()).to.be.closeTo(nowTime, 2);
expect(client.getLastPing()).toBeCloseTo(nowTime, 2);
});
});

View File

@ -0,0 +1,117 @@
import { describe, expect, it } from "@jest/globals";
import { Client } from "../../../../src/models/client";
import { TransmissionHandler } from "../../../../src/messageHandler/handlers";
import { Realm } from "../../../../src/models/realm";
import { MessageType } from "../../../../src/enums";
import type WebSocket from "ws";
const createFakeSocket = (): WebSocket => {
/* eslint-disable @typescript-eslint/no-empty-function */
const sock = {
send: (): void => {},
close: (): void => {},
on: (): void => {},
};
/* eslint-enable @typescript-eslint/no-empty-function */
return sock as unknown as WebSocket;
};
describe("Transmission handler", () => {
it("should save message in queue when destination client not connected", () => {
const realm = new Realm();
const handleTransmission = TransmissionHandler({ realm });
const clientFrom = new Client({ id: "id1", token: "" });
const idTo = "id2";
realm.setClient(clientFrom, clientFrom.getId());
handleTransmission(clientFrom, {
type: MessageType.OFFER,
src: clientFrom.getId(),
dst: idTo,
});
expect(realm.getMessageQueueById(idTo)?.getMessages().length).toBe(1);
});
it("should not save LEAVE and EXPIRE messages in queue when destination client not connected", () => {
const realm = new Realm();
const handleTransmission = TransmissionHandler({ realm });
const clientFrom = new Client({ id: "id1", token: "" });
const idTo = "id2";
realm.setClient(clientFrom, clientFrom.getId());
handleTransmission(clientFrom, {
type: MessageType.LEAVE,
src: clientFrom.getId(),
dst: idTo,
});
handleTransmission(clientFrom, {
type: MessageType.EXPIRE,
src: clientFrom.getId(),
dst: idTo,
});
expect(realm.getMessageQueueById(idTo)).toBeUndefined();
});
it("should send message to destination client when destination client connected", () => {
const realm = new Realm();
const handleTransmission = TransmissionHandler({ realm });
const clientFrom = new Client({ id: "id1", token: "" });
const clientTo = new Client({ id: "id2", token: "" });
const socketTo = createFakeSocket();
clientTo.setSocket(socketTo);
realm.setClient(clientTo, clientTo.getId());
let sent = false;
socketTo.send = (): void => {
sent = true;
};
handleTransmission(clientFrom, {
type: MessageType.OFFER,
src: clientFrom.getId(),
dst: clientTo.getId(),
});
expect(sent).toBe(true);
});
it("should send LEAVE message to source client when sending to destination client failed", () => {
const realm = new Realm();
const handleTransmission = TransmissionHandler({ realm });
const clientFrom = new Client({ id: "id1", token: "" });
const clientTo = new Client({ id: "id2", token: "" });
const socketFrom = createFakeSocket();
const socketTo = createFakeSocket();
clientFrom.setSocket(socketFrom);
clientTo.setSocket(socketTo);
realm.setClient(clientFrom, clientFrom.getId());
realm.setClient(clientTo, clientTo.getId());
let sent = false;
socketFrom.send = (data: string): void => {
if (JSON.parse(data)?.type === MessageType.LEAVE) {
sent = true;
}
};
socketTo.send = (): void => {
throw Error();
};
handleTransmission(clientFrom, {
type: MessageType.OFFER,
src: clientFrom.getId(),
dst: clientTo.getId(),
});
expect(sent).toBe(true);
});
});

View File

@ -1,6 +1,7 @@
import { expect } from 'chai';
import { describe, expect, it } from "@jest/globals";
import { HandlersRegistry } from '../../src/messageHandler/handlersRegistry';
import { Handler } from '../../src/messageHandler/handler';
import type { Handler } from '../../src/messageHandler/handler';
import { MessageType } from '../../src/enums';
describe('HandlersRegistry', () => {
@ -18,6 +19,6 @@ describe('HandlersRegistry', () => {
handlersRegistry.handle(undefined, { type: MessageType.OPEN, src: 'src', dst: 'dst' });
expect(handled).to.be.true;
expect(handled).toBe(true);
});
});

View File

@ -1,7 +1,8 @@
import { expect } from 'chai';
import { describe, expect, it } from "@jest/globals";
import { MessageQueue } from '../../src/models/messageQueue';
import { MessageType } from '../../src/enums';
import { IMessage } from '../../src/models/message';
import type { IMessage } from '../../src/models/message';
import { wait } from '../utils';
describe('MessageQueue', () => {
@ -17,14 +18,14 @@ describe('MessageQueue', () => {
it('should add message to queue', () => {
const queue = new MessageQueue();
queue.addMessage(createTestMessage());
expect(queue.getMessages().length).to.eq(1);
expect(queue.getMessages().length).toBe(1);
});
});
describe('#readMessage', () => {
it('should return undefined for empty queue', () => {
const queue = new MessageQueue();
expect(queue.readMessage()).to.be.undefined;
expect(queue.readMessage()).toBeUndefined();
});
it('should return message if any exists in queue', () => {
@ -32,8 +33,8 @@ describe('MessageQueue', () => {
const message = createTestMessage();
queue.addMessage(message);
expect(queue.readMessage()).to.deep.eq(message);
expect(queue.readMessage()).to.be.undefined;
expect(queue.readMessage()).toEqual(message);
expect(queue.readMessage()).toBeUndefined();
});
});
@ -42,7 +43,7 @@ describe('MessageQueue', () => {
const queue = new MessageQueue();
const lastReadAt = queue.getLastReadAt();
queue.readMessage();
expect(queue.getLastReadAt()).to.be.eq(lastReadAt);
expect(queue.getLastReadAt()).toBe(lastReadAt);
});
it('should be changed when read message', async () => {
@ -52,11 +53,11 @@ describe('MessageQueue', () => {
await wait(10);
expect(queue.getLastReadAt()).to.be.eq(lastReadAt);
expect(queue.getLastReadAt()).toBe(lastReadAt);
queue.readMessage();
expect(queue.getLastReadAt()).to.be.gte(lastReadAt + 10);
expect(queue.getLastReadAt()).toBeGreaterThanOrEqual(lastReadAt + 10);
});
});
});

View File

@ -1,4 +1,5 @@
import { expect } from 'chai';
import { describe, expect, it } from "@jest/globals";
import { Realm } from '../../src/models/realm';
import { Client } from '../../src/models/client';
@ -6,8 +7,8 @@ describe('Realm', () => {
describe('#generateClientId', () => {
it('should generate a 36-character UUID, or return function value', () => {
const realm = new Realm();
expect(realm.generateClientId().length).to.eq(36);
expect(realm.generateClientId(() => 'abcd')).to.eq('abcd');
expect(realm.generateClientId().length).toBe(36);
expect(realm.generateClientId(() => 'abcd')).toBe('abcd');
});
});
@ -17,7 +18,7 @@ describe('Realm', () => {
const client = new Client({ id: 'id', token: '' });
realm.setClient(client, 'id');
expect(realm.getClientsIds()).to.deep.eq(['id']);
expect(realm.getClientsIds()).toEqual(['id']);
});
});
@ -29,7 +30,7 @@ describe('Realm', () => {
realm.setClient(client, 'id');
realm.removeClientById('id');
expect(realm.getClientById('id')).to.be.undefined;
expect(realm.getClientById('id')).toBeUndefined();
});
});
@ -39,12 +40,12 @@ describe('Realm', () => {
const client = new Client({ id: 'id', token: '' });
realm.setClient(client, 'id');
expect(realm.getClientsIds()).to.deep.eq(['id']);
expect(realm.getClientsIds()).toEqual(['id']);
expect(realm.getClientById('id')).to.eq(client);
expect(realm.getClientById('id')).toBe(client);
realm.removeClientById('id');
expect(realm.getClientsIds()).to.deep.eq([]);
expect(realm.getClientsIds()).toEqual([]);
});
});
});

View File

@ -1,4 +1,5 @@
import { expect } from 'chai';
import { describe, expect, it } from "@jest/globals";
import http from 'http';
import expectedJson from '../app.json';
import { spawn } from 'child_process';
@ -27,7 +28,8 @@ async function makeRequest() {
}
describe('Check bin/peerjs', () => {
it('should return content of app.json file', async () => {
it('should return content of app.json file', async () => {
expect.assertions(1);
let resolver: () => void;
let rejecter: (err: unknown) => void;
const promise = new Promise<void>((resolve, reject) => {
@ -36,18 +38,17 @@ describe('Check bin/peerjs', () => {
});
const ls = spawn('node', [path.join(__dirname, '../', 'dist/bin/peerjs.js'), '--port', PORT]);
ls.stdout.on('data', async (data: string) => {
ls.stdout.on('data', async (data: string) => {
if (!data.includes('Started')) return;
try {
const resp = await makeRequest();
expect(resp).to.deep.eq(expectedJson);
expect(resp).toEqual(expectedJson);
resolver();
} catch (error) {
rejecter(error);
} finally {
ls.kill('SIGINT');
ls.kill('SIGKILL');
}
});

View File

@ -1,4 +1,5 @@
import { expect } from 'chai';
import { describe, expect, it } from "@jest/globals";
import { Client } from '../../../src/models/client';
import { Realm } from '../../../src/models/realm';
import { CheckBrokenConnections } from '../../../src/services/checkBrokenConnections';
@ -16,7 +17,7 @@ describe('CheckBrokenConnections', () => {
await wait(checkBrokenConnections.checkInterval * 2 + 30);
expect(realm.getClientById('id')).to.be.undefined;
expect(realm.getClientById('id')).toBeUndefined();
checkBrokenConnections.stop();
});
@ -37,7 +38,7 @@ describe('CheckBrokenConnections', () => {
await wait(checkBrokenConnections.checkInterval * 2 + 10);
expect(realm.getClientById('id')).to.be.undefined;
expect(realm.getClientById('id')).toBeUndefined();
checkBrokenConnections.stop();
});

View File

@ -1,7 +1,8 @@
import { expect } from 'chai';
import { describe, expect, it } from "@jest/globals";
import { Client } from '../../../src/models/client';
import { Realm } from '../../../src/models/realm';
import { IMessage } from '../../../src/models/message';
import type { IMessage } from '../../../src/models/message';
import { MessagesExpire } from '../../../src/services/messagesExpire';
import { MessageHandler } from '../../../src/messageHandler';
import { MessageType } from '../../../src/enums';
@ -33,11 +34,11 @@ describe('MessagesExpire', () => {
await wait(checkInterval * 2);
expect(realm.getMessageQueueById(client.getId())?.getMessages().length).to.be.eq(1);
expect(realm.getMessageQueueById(client.getId())?.getMessages().length).toBe(1);
await wait(expireTimeout);
expect(realm.getMessageQueueById(client.getId())).to.be.undefined;
expect(realm.getMessageQueueById(client.getId())).toBeUndefined();
messagesExpire.stopMessagesExpiration();
});
@ -59,8 +60,8 @@ describe('MessagesExpire', () => {
let handledCount = 0;
messageHandler.handle = (client, message): boolean => {
expect(client).to.be.undefined;
expect(message.type).to.be.eq(MessageType.EXPIRE);
expect(client).toBeUndefined();
expect(message.type).toBe(MessageType.EXPIRE);
handledCount++;
@ -72,7 +73,7 @@ describe('MessagesExpire', () => {
await wait(checkInterval * 2);
await wait(expireTimeout);
expect(handledCount).to.be.eq(2);
expect(handledCount).toBe(2);
messagesExpire.stopMessagesExpiration();
});

View File

@ -1,4 +1,5 @@
import { expect } from 'chai';
import { describe, expect, it } from "@jest/globals";
import { Server, WebSocket } from 'mock-socket';
import type {Server as HttpServer} from 'node:http';
import { Realm } from '../../../src/models/realm';
@ -107,11 +108,11 @@ describe('WebSocketServer', () => {
const webSocketServer = new WebSocketServer({ server, realm, config });
expect(webSocketServer.path).to.be.eq('/peerjs');
expect(webSocketServer.path).toBe('/peerjs');
const webSocketServer2 = new WebSocketServer({ server: server2, realm, config: config2 });
expect(webSocketServer2.path).to.be.eq('path/peerjs');
expect(webSocketServer2.path).toBe('path/peerjs');
server.stop();
server2.stop();
@ -136,10 +137,10 @@ describe('WebSocketServer', () => {
return errorSent;
};
expect(await getError(fakeURL)).to.be.true;
expect(await getError(`${fakeURL}?key=${config.key}`)).to.be.true;
expect(await getError(`${fakeURL}?key=${config.key}&id=1`)).to.be.true;
expect(await getError(`${fakeURL}?key=notValidKey&id=userId&token=userToken`, Errors.INVALID_KEY)).to.be.true;
expect(await getError(fakeURL)).toBe(true);
expect(await getError(`${fakeURL}?key=${config.key}`)).toBe(true);
expect(await getError(`${fakeURL}?key=${config.key}&id=1`)).toBe(true);
expect(await getError(`${fakeURL}?key=notValidKey&id=userId&token=userToken`, Errors.INVALID_KEY)).toBe(true);
});
it(`should check concurrent limit`, async () => {
@ -171,24 +172,24 @@ describe('WebSocketServer', () => {
const c1 = createClient('1');
expect(await checkOpen(c1)).to.be.true;
expect(await checkOpen(c1)).toBe(true);
const c2 = createClient('2');
expect(await checkSequence(c2, [
{ type: MessageType.ERROR, error: Errors.CONNECTION_LIMIT_EXCEED }
])).to.be.true;
])).toBe(true);
await c1.destroy?.();
await c2.destroy?.();
await wait(10);
expect(realm.getClientsIds().length).to.be.eq(0);
expect(realm.getClientsIds().length).toBe(0);
const c3 = createClient('3');
expect(await checkOpen(c3)).to.be.true;
expect(await checkOpen(c3)).toBe(true);
await c3.destroy?.();
});

9
jest.config.ts Normal file
View File

@ -0,0 +1,9 @@
import type { Config } from "jest";
const config: Config = {
transform: {
"^.+\\.(t|j)sx?$": "@swc/jest",
},
};
export default config;

3472
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -25,9 +25,9 @@
"types": "./dist/peer.d.ts",
"default": "./dist/module.mjs"
},
"require":{
"require": {
"types": "./dist/peer.d.ts",
"default": "./dist/index.cjs"
"default": "./dist/index.cjs"
}
}
},
@ -64,9 +64,8 @@
"build": "parcel build",
"lint": "eslint --ext .js,.ts . && npm run check",
"check": "tsc --noEmit",
"test": "npm run lint && mocha",
"coverage": "nyc mocha",
"coverage:lcov": "nyc --reporter=lcov mocha",
"test": "npm run lint && jest",
"coverage": "jest --coverage",
"start": "node dist/bin/peerjs.js --port ${PORT:=9000}",
"dev": "nodemon --watch src -e ts --exec 'npm run build && npm run start'",
"semantic-release": "semantic-release"
@ -81,29 +80,24 @@
},
"devDependencies": {
"@codedependant/semantic-release-docker": "^4.3.0",
"@istanbuljs/nyc-config-typescript": "^1.0.2",
"@parcel/packager-ts": "^2.8.2",
"@parcel/transformer-typescript-types": "^2.8.2",
"@semantic-release/changelog": "^6.0.1",
"@semantic-release/git": "^10.0.1",
"@swc/core": "^1.3.35",
"@swc/jest": "^0.2.24",
"@tsconfig/node16-strictest-esm": "^1.0.3",
"@types/chai": "^4.2.11",
"@types/cors": "^2.8.6",
"@types/mocha": "^10.0.0",
"@types/jest": "^29.4.0",
"@types/node": "^14.18.33",
"@types/yargs": "^17.0.19",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"chai": "^4.2.0",
"eslint": "^8.0.0",
"mocha": "^10.1.0",
"jest": "^29.4.2",
"mock-socket": "^9.1.5",
"nodemon": "^2.0.20",
"nyc": "^15.1.0",
"parcel": "^2.8.2",
"semantic-release": "^20.0.0",
"source-map-support": "^0.5.21",
"ts-node": "^10.0.0",
"typescript": "^4.1.2"
}
}

View File

@ -1,96 +0,0 @@
import { expect } from 'chai';
import { Client } from '../../../../src/models/client';
import { TransmissionHandler } from '../../../../src/messageHandler/handlers';
import { Realm } from '../../../../src/models/realm';
import { MessageType } from '../../../../src/enums';
import type WebSocket from "ws";
const createFakeSocket = (): WebSocket => {
/* eslint-disable @typescript-eslint/no-empty-function */
const sock = {
send: (): void => { },
close: (): void => { },
on: (): void => { },
};
/* eslint-enable @typescript-eslint/no-empty-function */
return (sock as unknown as WebSocket);
};
describe('Transmission handler', () => {
it('should save message in queue when destination client not connected', () => {
const realm = new Realm();
const handleTransmission = TransmissionHandler({ realm });
const clientFrom = new Client({ id: 'id1', token: '' });
const idTo = 'id2';
realm.setClient(clientFrom, clientFrom.getId());
handleTransmission(clientFrom, { type: MessageType.OFFER, src: clientFrom.getId(), dst: idTo });
expect(realm.getMessageQueueById(idTo)?.getMessages().length).to.be.eq(1);
});
it('should not save LEAVE and EXPIRE messages in queue when destination client not connected', () => {
const realm = new Realm();
const handleTransmission = TransmissionHandler({ realm });
const clientFrom = new Client({ id: 'id1', token: '' });
const idTo = 'id2';
realm.setClient(clientFrom, clientFrom.getId());
handleTransmission(clientFrom, { type: MessageType.LEAVE, src: clientFrom.getId(), dst: idTo });
handleTransmission(clientFrom, { type: MessageType.EXPIRE, src: clientFrom.getId(), dst: idTo });
expect(realm.getMessageQueueById(idTo)).to.be.undefined;
});
it('should send message to destination client when destination client connected', () => {
const realm = new Realm();
const handleTransmission = TransmissionHandler({ realm });
const clientFrom = new Client({ id: 'id1', token: '' });
const clientTo = new Client({ id: 'id2', token: '' });
const socketTo = createFakeSocket();
clientTo.setSocket(socketTo);
realm.setClient(clientTo, clientTo.getId());
let sent = false;
socketTo.send = (): void => {
sent = true;
};
handleTransmission(clientFrom, { type: MessageType.OFFER, src: clientFrom.getId(), dst: clientTo.getId() });
expect(sent).to.be.true;
});
it('should send LEAVE message to source client when sending to destination client failed', () => {
const realm = new Realm();
const handleTransmission = TransmissionHandler({ realm });
const clientFrom = new Client({ id: 'id1', token: '' });
const clientTo = new Client({ id: 'id2', token: '' });
const socketFrom = createFakeSocket();
const socketTo = createFakeSocket();
clientFrom.setSocket(socketFrom);
clientTo.setSocket(socketTo);
realm.setClient(clientFrom, clientFrom.getId());
realm.setClient(clientTo, clientTo.getId());
let sent = false;
socketFrom.send = (data: string): void => {
if (JSON.parse(data)?.type === MessageType.LEAVE) {
sent = true;
}
};
socketTo.send = (): void => {
throw Error();
};
handleTransmission(clientFrom, { type: MessageType.OFFER, src: clientFrom.getId(), dst: clientTo.getId() });
expect(sent).to.be.true;
});
});

View File

@ -9,7 +9,8 @@
"exactOptionalPropertyTypes": false
},
"include": [
"./src/**/*"
"./src/**/*",
"__test__/**/*"
],
"exclude": [
"test",