Skip to content
This repository has been archived by the owner on Mar 1, 2024. It is now read-only.

Unit tests for library #156

Merged
merged 54 commits into from
Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
6e13e39
add unit test coverage reports to gitignore
hmuurine Mar 10, 2023
5b135fb
add jest dependencies
hmuurine Mar 10, 2023
af00134
add jest config
hmuurine Mar 10, 2023
44ac084
add Config unit tests
hmuurine Mar 10, 2023
1f171fb
add AFKController unit tests
hmuurine Mar 10, 2023
63f664b
mock video codecs list
hmuurine Mar 10, 2023
2001877
test for PixelStreaming SettingsChangedEvent
hmuurine Mar 10, 2023
1b156a1
mock/unmock functions for RTCRtpReceiver
hmuurine Mar 14, 2023
948f4a8
mock WebSocket
hmuurine Mar 14, 2023
5a6800d
test for PixelStreaming.connect()
hmuurine Mar 14, 2023
5223a38
unit test for disconnect
hmuurine Mar 14, 2023
1c72ef1
unit tests for reconnect
hmuurine Mar 14, 2023
bfae6e3
added eventemitter events in tests
hmuurine Mar 14, 2023
c974a8a
test that listStreamers is sent on WS connect
hmuurine Mar 14, 2023
3979503
mock RTCPeerConnection and RTCIceCandidate
hmuurine Mar 14, 2023
1d643c2
test webRtcConnected event
hmuurine Mar 14, 2023
7aed7b4
test RTCPeerConnect close
hmuurine Mar 14, 2023
da57fe9
fixed variable name for consistency
hmuurine Mar 14, 2023
d0841e9
check for navigator.getGamepads before calling it
hmuurine Mar 14, 2023
4856f77
mock addTransceiver, createAnswer, getTransceivers, setRemoteDescription
hmuurine Mar 14, 2023
535c1e3
test for receiving a connection offer
hmuurine Mar 14, 2023
95a78b5
add null checks for peerConnection since it might be null if connecti…
hmuurine Mar 14, 2023
c488c1e
mock addIceCandidate
hmuurine Mar 14, 2023
7c364ee
test for receiving ICE candidate message
hmuurine Mar 14, 2023
b6ce995
mock setLocalDescription, getStats
hmuurine Mar 14, 2023
6d7b1a4
test for statistics
hmuurine Mar 14, 2023
80c062b
test webRtcDisconnected event
hmuurine Mar 14, 2023
9f73b2d
mock RTCDataChannel and RTCDataChannelEvent
hmuurine Mar 14, 2023
7b25fce
test for dataChannelOpen event
hmuurine Mar 14, 2023
fa2e5b4
let -> const
hmuurine Mar 14, 2023
6669f62
mock RTCTrackEvent
hmuurine Mar 14, 2023
bf4cd72
mock MediaStream and MediaStreamTrack
hmuurine Mar 14, 2023
7c2706b
mock video element play()
hmuurine Mar 14, 2023
17e4353
test playStream and playStreamRejected events
hmuurine Mar 14, 2023
877c584
mock video readyState
hmuurine Mar 14, 2023
d0f2203
mock WebRTC data channel send()
hmuurine Mar 14, 2023
6c9e7cb
test emitCommand
hmuurine Mar 14, 2023
df0be34
test emitUIInteraction
hmuurine Mar 14, 2023
4bb0316
test emitConsoleCommand
hmuurine Mar 14, 2023
50eda2c
Jest mock for HTMLMediaElement.play()
hmuurine Mar 15, 2023
a2e5e74
check boolean return value of all emit* commands
hmuurine Mar 15, 2023
376aa0e
test dataChannelClose event
hmuurine Mar 15, 2023
d13c7cb
added TextEncoder and TextDecoder to Jest globals
hmuurine Mar 15, 2023
a5b903b
mock datachannel onmessage
hmuurine Mar 15, 2023
a42904c
test UE -> browser data channel message
hmuurine Mar 15, 2023
eff46b0
clarified in test description that we are using a Response message
hmuurine Mar 15, 2023
064275d
extracted commonly used setup steps as util functions
hmuurine Mar 16, 2023
57c58b2
triggerSdpOffer -> triggerSdpOfferMessage for consistency
hmuurine Mar 16, 2023
4a69f21
run unit tests as a Github action
hmuurine Mar 16, 2023
05f5742
break one of the tests to test the GH action
hmuurine Mar 16, 2023
5801e41
Revert "break one of the tests to test the GH action"
hmuurine Mar 16, 2023
7a26f2f
run tests only if files changed under Frontend/library
hmuurine Mar 16, 2023
e0b6eeb
rename action to 'Run library unit tests' since now it's triggered on…
hmuurine Mar 16, 2023
a1c9f46
added unit test run instructions to README.md
hmuurine Mar 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ node_modules/
node.zip
SignallingWebServer/Public/
SignallingWebServer/certificates
.vscode
.vscode
coverage/
18 changes: 18 additions & 0 deletions Frontend/library/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
preset: "ts-jest/presets/js-with-ts",
testEnvironment: "jsdom",
transform: {
"^.+\\.tsx?$": [
"ts-jest",
{
tsconfig: "tsconfig.jest.json",
},
],
},
modulePathIgnorePatterns: ["<rootDir>/build/"],
testPathIgnorePatterns: ["<rootDir>/build/", "/node_modules/"],
globals: {
TextDecoder: TextDecoder,
TextEncoder: TextEncoder,
}
};
10,708 changes: 8,314 additions & 2,394 deletions Frontend/library/package-lock.json

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion Frontend/library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@
"build": "npx webpack --config webpack.prod.js",
"build-dev": "npx webpack --config webpack.dev.js",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"test": "jest --detectOpenHandles --coverage=true",
"spellcheck": "cspell \"{README.md,.github/*.md,src/**/*.ts}\""
},
"devDependencies": {
"@types/jest": "29.4.0",
"@types/webxr": "^0.5.1",
"@typescript-eslint/eslint-plugin": "^5.16.0",
"@typescript-eslint/parser": "^5.16.0",
"@types/webxr": "^0.5.1",
"cspell": "^4.1.0",
"eslint": "^8.11.0",
"jest": "^29.4.0",
"jest-environment-jsdom": "29.4.0",
"prettier": "2.8.3",
"ts-jest": "29.0.5",
"ts-loader": "^9.4.2",
"typedoc": "^0.23.24",
"typescript": "^4.9.4",
Expand Down
162 changes: 162 additions & 0 deletions Frontend/library/src/AFK/AFKController.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import { Config, Flags, NumericParameters } from '../Config/Config';
import { PixelStreaming } from '../PixelStreaming/PixelStreaming';
import { AfkTimedOutEvent, AfkWarningActivateEvent, AfkWarningUpdateEvent, AfkWarningDeactivateEvent } from '../Util/EventEmitter';
import { mockRTCRtpReceiver, unmockRTCRtpReceiver } from '../__test__/mockRTCRtpReceiver';
import {
AFKController
} from './AFKController';

describe('AFKController', () => {
let mockPixelStreaming: PixelStreaming;

beforeEach(() => {
mockRTCRtpReceiver();
jest.useFakeTimers();
mockPixelStreaming = {
dispatchEvent: jest.fn()
} as any as PixelStreaming;
});

afterEach(() => {
unmockRTCRtpReceiver();
jest.resetAllMocks();
});

it('should not activate AFK timer if it has been disabled from settings', () => {
const config = new Config({ initialSettings: { [Flags.AFKDetection]: false } });
const onDismissAfk = jest.fn();
const afkController = new AFKController(config, mockPixelStreaming, onDismissAfk);

afkController.startAfkWarningTimer();
expect(afkController.active).toBe(false);

jest.advanceTimersByTime(1000000 * 1000);
expect(mockPixelStreaming.dispatchEvent).not.toHaveBeenCalled();
});

it('should activate AFK timer and trigger it after specified delay if it has been enabled from settings', () => {
const timeoutSeconds = 100;

const config = new Config({ initialSettings: { [Flags.AFKDetection]: true, [NumericParameters.AFKTimeoutSecs]: timeoutSeconds} });
const onDismissAfk = jest.fn();
const afkController = new AFKController(config, mockPixelStreaming, onDismissAfk);

afkController.startAfkWarningTimer();
expect(afkController.active).toBe(true);

// Advance to 1 second before AFK event:
jest.advanceTimersByTime((timeoutSeconds - 1) * 1000);
expect(mockPixelStreaming.dispatchEvent).not.toHaveBeenCalled();

// advance 1 more second to trigger AFK warning
jest.advanceTimersByTime(1000);
expect(mockPixelStreaming.dispatchEvent).toHaveBeenCalledWith(new AfkWarningActivateEvent({
countDown: 0,
dismissAfk: expect.anything()
}));
expect(mockPixelStreaming.dispatchEvent).toHaveBeenCalledWith(new AfkWarningUpdateEvent({
countDown: 10,
}));

// advance 10 more seconds to trigger AFK countdown updates and eventually timeout
jest.advanceTimersByTime(10 * 1000);
expect(mockPixelStreaming.dispatchEvent).toHaveBeenCalledWith(new AfkWarningUpdateEvent({
countDown: 9,
}));
expect(mockPixelStreaming.dispatchEvent).toHaveBeenCalledWith(new AfkWarningUpdateEvent({
countDown: 8,
}));
expect(mockPixelStreaming.dispatchEvent).toHaveBeenCalledWith(new AfkWarningUpdateEvent({
countDown: 7,
}));
expect(mockPixelStreaming.dispatchEvent).toHaveBeenCalledWith(new AfkWarningUpdateEvent({
countDown: 6,
}));
expect(mockPixelStreaming.dispatchEvent).toHaveBeenCalledWith(new AfkWarningUpdateEvent({
countDown: 5,
}));
expect(mockPixelStreaming.dispatchEvent).toHaveBeenCalledWith(new AfkWarningUpdateEvent({
countDown: 4,
}));
expect(mockPixelStreaming.dispatchEvent).toHaveBeenCalledWith(new AfkWarningUpdateEvent({
countDown: 3,
}));
expect(mockPixelStreaming.dispatchEvent).toHaveBeenCalledWith(new AfkWarningUpdateEvent({
countDown: 2,
}));
expect(mockPixelStreaming.dispatchEvent).toHaveBeenCalledWith(new AfkWarningUpdateEvent({
countDown: 1,
}));
expect(mockPixelStreaming.dispatchEvent).toHaveBeenCalledWith(new AfkTimedOutEvent());
});

it('should postpone AFK activation each time resetAfkWarningTimer is called', () => {
const timeoutSeconds = 100;

const config = new Config({ initialSettings: { [Flags.AFKDetection]: true, [NumericParameters.AFKTimeoutSecs]: timeoutSeconds} });
const onDismissAfk = jest.fn();
const afkController = new AFKController(config, mockPixelStreaming, onDismissAfk);

afkController.startAfkWarningTimer();

// Advance to 1 second before AFK event:
jest.advanceTimersByTime((timeoutSeconds - 1) * 1000);
expect(mockPixelStreaming.dispatchEvent).not.toHaveBeenCalled();

afkController.resetAfkWarningTimer();

// advance 1 more second and ensure that AFK warning is not triggered since reset was called
jest.advanceTimersByTime(1000);
expect(mockPixelStreaming.dispatchEvent).not.toHaveBeenCalled();

// reset AFK timer once more and ensure it is triggered exactly after timeoutSeconds
afkController.resetAfkWarningTimer();

// Advance to 1 second before AFK event:
jest.advanceTimersByTime((timeoutSeconds - 1) * 1000);
expect(mockPixelStreaming.dispatchEvent).not.toHaveBeenCalled();

// advance 1 more second to trigger AFK warning
jest.advanceTimersByTime(1000);
expect(mockPixelStreaming.dispatchEvent).toHaveBeenCalledWith(new AfkWarningActivateEvent({
countDown: 0,
dismissAfk: expect.anything()
}));
});

it('should dismiss AFK warning countdown if onAfkClick is called', () => {
const timeoutSeconds = 100;

const config = new Config({ initialSettings: { [Flags.AFKDetection]: true, [NumericParameters.AFKTimeoutSecs]: timeoutSeconds} });
const onDismissAfk = jest.fn();
const afkController = new AFKController(config, mockPixelStreaming, onDismissAfk);

afkController.startAfkWarningTimer();

// Advance to AFK event:
jest.advanceTimersByTime(timeoutSeconds * 1000);
expect(mockPixelStreaming.dispatchEvent).toHaveBeenCalledWith(new AfkWarningActivateEvent({
countDown: 0,
dismissAfk: expect.anything()
}));
expect(mockPixelStreaming.dispatchEvent).toHaveBeenCalledWith(new AfkWarningUpdateEvent({
countDown: 10,
}));

// Advance one more second and call onAfkClick
jest.advanceTimersByTime(1000);

expect(mockPixelStreaming.dispatchEvent).toHaveBeenCalledWith(new AfkWarningUpdateEvent({
countDown: 9,
}));

afkController.onAfkClick();
expect(mockPixelStreaming.dispatchEvent).toHaveBeenCalledWith(new AfkWarningDeactivateEvent());

// advance 10 more seconds and ensure there are no more countdown/timeout events emitted
jest.advanceTimersByTime(10 * 1000);
expect(mockPixelStreaming.dispatchEvent).toHaveBeenCalledTimes(4);
});


});
Loading