优化目录结构

This commit is contained in:
2026-05-25 20:37:36 +08:00
parent bbe7e71274
commit 40fd7f7e08
101 changed files with 108 additions and 110 deletions

View File

@@ -0,0 +1,172 @@
import {
FourCC,
Mouse,
Keyboard,
Touchscreen,
Gamepad,
KeyboardState,
MouseState,
TouchscreenState,
GamepadState,
StateEvent,
InputEvent,
TextEvent
} from "../../src/input/inputdevice.js";
describe(`FourCC`, () => {
test('toInt32', () => {
const number = new FourCC('A', 'A', 'A', 'A').toInt32();
expect(number).toBe(0x41414141);
});
});
describe(`MouseState`, () => {
describe(`with MouseEvent`, () => {
let event;
beforeEach(() => {
event = new MouseEvent('click', { buttons:1, clientX:0, clientY:0});
});
test('format', () => {
const format = new MouseState(event).format;
expect(format).toBe(0x4d4f5553);
});
test('buffer', () => {
const state = new MouseState(event);
expect(state.buffer.byteLength).toBeGreaterThan(0);
});
});
describe(`with WheelEvent`, () => {
let event;
beforeEach(() => {
event = new WheelEvent('wheel', { deltaX:0, deltaY:0 });
});
test('format', () => {
const format = new MouseState(event).format;
expect(format).toBe(0x4d4f5553);
});
test('buffer', () => {
const state = new MouseState(event);
expect(state.buffer.byteLength).toBeGreaterThan(0);
});
});
});
describe(`KeyboardState`, () => {
let event;
beforeEach(() => {
event = new KeyboardEvent('keydown', { code: 'KeyA' });
});
test('format', () => {
const format = new KeyboardState(event).format;
expect(format).toBe(0x4b455953);
});
test('buffer', () => {
const state = new KeyboardState(event);
expect(state.buffer.byteLength).toBeGreaterThan(0);
});
});
describe(`TouchscreenState`, () => {
let event;
beforeEach(() => {
event = new TouchEvent("touchstart", {
changedTouches: [{ // InputInit
identifier: 0,
target: null,
clientX: 0,
clientY: 0,
screenX: 0,
screenY: 0,
pageX: 0,
pageY: 0,
radiusX: 0,
radiusY: 0,
rotationAngle: 0,
force: 0,
altitudeAngle: 0,
azimuthAngle:0,
touchType: "direct"
}]
});
});
test('format', () => {
const format = new TouchscreenState(event, null, Date.now()).format;
expect(format).toBe(0x54534352);
});
test('buffer', () => {
const state = new TouchscreenState(event, null, Date.now());
expect(state.buffer.byteLength).toBeGreaterThan(0);
});
});
describe(`GamepadState`, () => {
let event;
beforeEach(() => {
event = {
type: 'gamepadupdated',
gamepad : {
id: 1,
buttons: Array(16).fill({ pressed: false, value: 1 }),
axes:[0, 0, 0, 0]
}};
});
test('format', () => {
const format = new GamepadState(event).format;
expect(format).toBe(0x47504144);
});
test('buffer', () => {
const state = new GamepadState(event);
expect(state.buffer.byteLength).toBeGreaterThan(0);
});
});
describe(`StateEvent`, () => {
let state;
beforeEach(() => {
const event = new KeyboardEvent('keydown', { code: 'KeyA' });
state = new KeyboardState(event);
});
test('buffer', () => {
const stateEvent = StateEvent.fromState(state, 0, Date.now());
expect(new Int32Array(stateEvent.buffer.slice(0, 4))[0]).toBe(StateEvent.format);
});
});
describe(`TextEvent`, () => {
test('buffer', () => {
const event = new KeyboardEvent('keydown', { code: 'KeyA', key: "a"});
const textEvent = TextEvent.create(0, event, Date.now());
expect(new Int32Array(textEvent.buffer.slice(0, 4))[0]).toBe(TextEvent.format);
const offset = InputEvent.size;
// 'a' is 97
expect(new Uint32Array(textEvent.buffer.slice(offset, offset+4))[0]).toBe(97);
});
});
describe(`Mouse`, () => {
test('alignedSizeInBytes', () => {
let device = new Mouse("Mouse", "Mouse", 1, null, null);
expect(device).toBeInstanceOf(Mouse);
});
});
describe(`Keyboard`, () => {
test('alignedSizeInBytes', () => {
let device = new Keyboard("Keyboard", "Keyboard", 1, null, null);
expect(device).toBeInstanceOf(Keyboard);
});
});
describe(`Touchscreen`, () => {
test('alignedSizeInBytes', () => {
let device = new Touchscreen("Touchscreen", "Touchscreen", 1, null, null);
expect(device).toBeInstanceOf(Touchscreen);
});
});
describe(`Gamepad`, () => {
test('alignedSizeInBytes', () => {
let device = new Gamepad("Gamepad", "Gamepad", 1, null, null);
expect(device).toBeInstanceOf(Gamepad);
});
});

View File

@@ -0,0 +1,132 @@
import {
InputDevice,
MouseState,
KeyboardState,
TouchscreenState,
GamepadState
} from "../../src/input/inputdevice.js";
import {
MessageType,
NewDeviceMsg,
NewEventsMsg,
RemoveDeviceMsg,
InputRemoting,
} from "../../src/input/inputremoting.js";
import {
Sender,
Observer
} from "../../src/core/sender.js";
import {DOMRect} from "../helpers/domrect.js";
describe(`InputRemoting`, () => {
let sender = null;
let inputRemoting = null;
let observer = null;
beforeEach(async () => {
document.getBoundingClientRect = function(){ return new DOMRect(0,0,0,0); };
sender = new Sender(document);
inputRemoting = new InputRemoting(sender);
let dc = null;
observer = new Observer(dc);
});
test('startSending', () => {
expect.assertions(0);
inputRemoting.startSending();
});
test('stopSending', () => {
expect.assertions(0);
inputRemoting.startSending();
inputRemoting.stopSending();
});
test('subscribe', () => {
expect.assertions(0);
inputRemoting.subscribe(observer);
});
});
test('create NewDeviceMsg', () => {
const device = new InputDevice("Keyboard", "Keyboard", 0, null, null);
const msg = NewDeviceMsg.create(device);
expect(msg.participant_id).toBe(0);
expect(msg.type).toBe(MessageType.NewDevice);
expect(msg.data).toBeInstanceOf(ArrayBuffer);
expect(msg.data.byteLength).toBeGreaterThan(0);
});
describe('create NewEventMsg', () => {
test('using MouseState', () => {
const event = new MouseEvent('click', { buttons:0, clientX:0, clientY:0} );
const state = new MouseState(event);
const msg = NewEventsMsg.create(state);
expect(msg.participant_id).toBe(0);
expect(msg.type).toBe(MessageType.NewEvents);
expect(msg.data).toBeInstanceOf(ArrayBuffer);
expect(msg.data.byteLength).toBeGreaterThan(0);
});
test('using KeyboardState', () => {
const event = new KeyboardEvent("keydown", { code: 'KeyA' });
const state = new KeyboardState(event);
const msg = NewEventsMsg.create(state);
expect(msg.participant_id).toBe(0);
expect(msg.type).toBe(MessageType.NewEvents);
expect(msg.data).toBeInstanceOf(ArrayBuffer);
expect(msg.data.byteLength).toBeGreaterThan(0);
});
test('using TouchscreenState', () => {
const event = new TouchEvent("touchstart", {
changedTouches: [{ // InputInit
identifier: 0,
target: null,
clientX: 0,
clientY: 0,
screenX: 0,
screenY: 0,
pageX: 0,
pageY: 0,
radiusX: 0,
radiusY: 0,
rotationAngle: 0,
force: 0,
altitudeAngle: 0,
azimuthAngle:0,
touchType: "direct"
}]
});
const state = new TouchscreenState(event, null, Date.now());
expect(state.touchData).not.toBeNull();
expect(state.touchData).toHaveLength(1);
const msg = NewEventsMsg.create(state.touchData[0]);
expect(msg.participant_id).toBe(0);
expect(msg.type).toBe(MessageType.NewEvents);
expect(msg.data).toBeInstanceOf(ArrayBuffer);
expect(msg.data.byteLength).toBeGreaterThan(0);
});
test('using GamepadState', () => {
const event = {
type: 'gamepadupdated',
gamepad : {
id: 1,
buttons: Array(16).fill({ pressed: false, value: 1 }),
axes:[1, 1, 1, 1]
}};
const state = new GamepadState(event);
const msg = NewEventsMsg.create(state);
expect(msg.participant_id).toBe(0);
expect(msg.type).toBe(MessageType.NewEvents);
expect(msg.data).toBeInstanceOf(ArrayBuffer);
expect(msg.data.byteLength).toBeGreaterThan(0);
});
});
test('create RemoveDeviceMsg', () => {
const device = new InputDevice("Keyboard", "Keyboard", 0, null, null);
const msg = RemoveDeviceMsg.create(device);
expect(msg.participant_id).toBe(0);
expect(msg.type).toBe(MessageType.RemoveDevice);
expect(msg.data).toBeInstanceOf(ArrayBuffer);
expect(msg.data.byteLength).toBeGreaterThan(0);
});

View File

@@ -0,0 +1,123 @@
import { MeetingRecorder } from '../../public/call/media/meeting-recorder.js';
class MediaStreamMock {
constructor(tracks = []) {
this.tracks = tracks;
}
getTracks() {
return this.tracks;
}
getAudioTracks() {
return this.tracks.filter(track => track.kind === 'audio');
}
getVideoTracks() {
return this.tracks.filter(track => track.kind === 'video');
}
}
class MediaRecorderMock {
static isTypeSupported() {
return true;
}
constructor(stream, options) {
this.stream = stream;
this.mimeType = options.mimeType;
this.state = 'inactive';
}
start() {
this.state = 'recording';
}
stop() {
this.state = 'inactive';
this.ondataavailable({ data: new Blob(['recording'], { type: this.mimeType }) });
this.onstop();
}
}
function createTrack(kind) {
return {
kind,
readyState: 'live',
stop: jest.fn(),
clone: jest.fn(() => createTrack(kind))
};
}
function createWindowMock({ mediaRecorder = MediaRecorderMock } = {}) {
return {
MediaRecorder: mediaRecorder,
MediaStream: MediaStreamMock,
URL: {
createObjectURL: jest.fn(() => 'blob:recording'),
revokeObjectURL: jest.fn()
},
requestAnimationFrame: jest.fn(() => 1),
cancelAnimationFrame: jest.fn(),
setTimeout: jest.fn((callback) => {
callback();
return 1;
})
};
}
describe('MeetingRecorder', () => {
beforeEach(() => {
HTMLCanvasElement.prototype.getContext = jest.fn(() => ({
fillStyle: '',
font: '',
textAlign: '',
textBaseline: '',
fillRect: jest.fn(),
fillText: jest.fn(),
drawImage: jest.fn()
}));
HTMLCanvasElement.prototype.captureStream = jest.fn(() => new MediaStreamMock([createTrack('video')]));
});
afterEach(() => {
jest.restoreAllMocks();
document.body.innerHTML = '';
});
test('starts and stops recording a meeting file', async () => {
const windowRef = createWindowMock();
const recorder = new MeetingRecorder({ documentRef: document, windowRef });
const localVideo = document.createElement('video');
localVideo.id = 'localVideo';
localVideo.srcObject = new MediaStreamMock([createTrack('video')]);
Object.defineProperty(localVideo, 'readyState', { value: 2 });
Object.defineProperty(localVideo, 'videoWidth', { value: 640 });
Object.defineProperty(localVideo, 'videoHeight', { value: 360 });
localVideo.getBoundingClientRect = () => ({ width: 320, height: 180 });
document.body.appendChild(localVideo);
await recorder.start({
localStream: new MediaStreamMock([createTrack('audio')]),
connectionId: '123-456-789'
});
expect(recorder.isRecording()).toBe(true);
const result = await recorder.stop();
expect(result.filename).toContain('meeting-recording-123-456-789');
expect(result.mimeType).toBe('video/mp4;codecs=avc1.42E01E,mp4a.40.2');
expect(result.filename).toMatch(/\.mp4$/);
expect(windowRef.URL.createObjectURL).not.toHaveBeenCalled();
expect(recorder.isRecording()).toBe(false);
});
test('reports unsupported browsers', async () => {
HTMLCanvasElement.prototype.captureStream = undefined;
const windowRef = createWindowMock({ mediaRecorder: undefined });
const recorder = new MeetingRecorder({ documentRef: document, windowRef });
await expect(recorder.start()).rejects.toThrow('当前浏览器不支持会议录制');
});
});

View File

@@ -0,0 +1,67 @@
import {
MemoryHelper
} from "../../src/utils/memoryhelper.js";
describe(`MemoryHelper.writeSingleBit`, () => {
test('turn on with offset 0', () => {
let bytes = new ArrayBuffer(3);
MemoryHelper.writeSingleBit(bytes, 0, false);
// check 00 00 00
const view = new Uint8Array(bytes);
expect(view[0]).toBe(0);
expect(view[1]).toBe(0);
expect(view[2]).toBe(0);
});
test('turn off with offset 0', () => {
let bytes = new ArrayBuffer(3);
MemoryHelper.writeSingleBit(bytes, 0, true);
// check 00 00 01
const view = new Uint8Array(bytes);
expect(view[0]).toBe(1);
expect(view[1]).toBe(0);
expect(view[2]).toBe(0);
MemoryHelper.writeSingleBit(bytes, 0, false);
// check 00 00 00
expect(view[0]).toBe(0);
expect(view[1]).toBe(0);
expect(view[2]).toBe(0);
});
test('turn on with offset 32', () => {
let bytes = new ArrayBuffer(3);
MemoryHelper.writeSingleBit(bytes, 8, true);
// check 00 01 00
const view = new Uint8Array(bytes);
expect(view[0]).toBe(0);
expect(view[1]).toBe(1);
expect(view[2]).toBe(0);
MemoryHelper.writeSingleBit(bytes, 0, true);
// check 00 01 01
expect(view[0]).toBe(1);
expect(view[1]).toBe(1);
expect(view[2]).toBe(0);
});
test('turn on with offset 15', () => {
let bytes = new ArrayBuffer(3);
MemoryHelper.writeSingleBit(bytes, 15, true);
// check 00 80 00
const view = new Uint8Array(bytes);
expect(view[0]).toBe(0);
expect(view[1]).toBe(128);
expect(view[2]).toBe(0);
MemoryHelper.writeSingleBit(bytes, 15, false);
// check 00 00 00
expect(view[0]).toBe(0);
expect(view[1]).toBe(0);
expect(view[2]).toBe(0);
});
});

View File

@@ -0,0 +1,250 @@
import Peer from "../../src/core/peer.js";
import { waitFor, sleep, getUniqueId, getRTCConfiguration } from "../helpers/testutils.js";
describe(`peer connection test`, () => {
const connectionId = "12345";
test(`constructor`, () => {
const peer = new Peer(connectionId, true);
expect(peer).not.toBeNull();
const rtcPeer = peer.pc;
expect(rtcPeer).not.toBeNull();
expect(rtcPeer.ontrack).not.toBeNull();
expect(rtcPeer.onicecandidate).not.toBeNull();
expect(rtcPeer.onnegotiationneeded).not.toBeNull();
expect(rtcPeer.onsignalingstatechange).not.toBeNull();
expect(rtcPeer.oniceconnectionstatechange).not.toBeNull();
expect(rtcPeer.onicegatheringstatechange).not.toBeNull();
});
test(`close peer`, async () => {
const config = getRTCConfiguration();
const peer = new Peer(connectionId, true, config);
expect(peer).not.toBeNull();
peer.close();
expect(peer.connectionId).toBeNull();
expect(peer.pc).toBeNull();
});
test(`transceiver direction is sendrecv if using addtrack`, () => {
const config = getRTCConfiguration();
const peer = new Peer(connectionId, true, config);
expect(peer).not.toBeNull();
const track = { id: getUniqueId(), kind: "audio" };
const sender = peer.addTrack(connectionId, track);
const transceiver = peer.getTransceivers(connectionId).find(t => t.sender == sender);
expect(transceiver.direction).toBe("sendrecv");
});
test(`fire trackevent when addtrack`, async () => {
let trackEvent;
const config = getRTCConfiguration();
const peer = new Peer(connectionId, true, config);
expect(peer).not.toBeNull();
peer.addEventListener('trackevent', (e) => trackEvent = e.detail);
const track = { id: getUniqueId(), kind: "audio" };
peer.addTrack(connectionId, track);
await waitFor(() => trackEvent != null);
expect(trackEvent.track).toBe(track);
});
test(`fire trackevent when on got offer description include track`, async () => {
let trackEvent;
const config = getRTCConfiguration();
const peer = new Peer(connectionId, true, config);
expect(peer).not.toBeNull();
peer.addEventListener('trackevent', (e) => trackEvent = e.detail);
const testDesc = { type: "offer", sdp: "newtracksdp" };
peer.onGotDescription(connectionId, testDesc);
await waitFor(() => trackEvent != null);
expect(trackEvent.track).not.toBeNull();
});
test(`fire sendoffer when addtrack`, async () => {
let offer;
const config = getRTCConfiguration();
const peer = new Peer(connectionId, true, config);
expect(peer).not.toBeNull();
peer.addEventListener('sendoffer', (e) => offer = e.detail);
const track = { id: getUniqueId(), kind: "audio" };
peer.addTrack(connectionId, track);
await waitFor(() => offer != null);
expect(offer.connectionId).toBe(connectionId);
});
test(`fire sendoffer when addTransceiver`, async () => {
let offer;
const config = getRTCConfiguration();
const peer = new Peer(connectionId, true, config);
expect(peer).not.toBeNull();
peer.addEventListener('sendoffer', (e) => offer = e.detail);
peer.addTransceiver(connectionId, "video");
await waitFor(() => offer != null);
expect(offer.connectionId).toBe(connectionId);
});
test(`fire sendoffer when createDataChannel`, async () => {
let offer;
const config = getRTCConfiguration();
const peer = new Peer(connectionId, true, config);
expect(peer).not.toBeNull();
peer.addEventListener('sendoffer', (e) => offer = e.detail);
peer.createDataChannel(connectionId, "testChannel");
await waitFor(() => offer != null);
expect(offer.connectionId).toBe(connectionId);
});
test(`re-fire sendoffer if get answer not yet`, async () => {
let sendOfferCount = 0;
let offer;
const config = getRTCConfiguration();
const peer = new Peer(connectionId, true, config, 100);
expect(peer).not.toBeNull();
peer.addEventListener('sendoffer', (e) => {
offer = e.detail;
sendOfferCount++;
});
const track = { id: getUniqueId(), kind: "audio" };
peer.addTrack(connectionId, track);
await waitFor(() => sendOfferCount > 2);
expect(offer.connectionId).toBe(connectionId);
expect(sendOfferCount).toBeGreaterThan(2);
});
test(`fire sendanswer when on got offer description in polite`, async () => {
let answer;
const config = getRTCConfiguration();
const peer = new Peer(connectionId, true, config);
expect(peer).not.toBeNull();
peer.addEventListener('sendanswer', (e) => answer = e.detail);
const testDesc = { type: "offer", sdp: "newtracksdp" };
peer.onGotDescription(connectionId, testDesc);
await waitFor(() => answer != null);
expect(answer.connectionId).toBe(connectionId);
});
test(`fire sendanswer when on got offer description in polite that have offer`, async () => {
let offer;
let answer;
const config = getRTCConfiguration();
const peer = new Peer(connectionId, true, config);
expect(peer).not.toBeNull();
peer.addEventListener('sendoffer', (e) => offer = e.detail);
peer.addEventListener('sendanswer', (e) => answer = e.detail);
const track = { id: getUniqueId(), kind: "audio" };
peer.addTrack(connectionId, track);
await waitFor(() => offer != null);
expect(offer.connectionId).toBe(connectionId);
const testDesc = { type: "offer", sdp: "newtracksdp" };
peer.onGotDescription(connectionId, testDesc);
await waitFor(() => answer != null);
expect(answer.connectionId).toBe(connectionId);
});
test(`fire sendanswer when on got offer description in impolite that don't have offer`, async () => {
let answer;
const config = getRTCConfiguration();
const peer = new Peer(connectionId, false, config);
expect(peer).not.toBeNull();
peer.addEventListener('sendanswer', (e) => answer = e.detail);
const testDesc = { type: "offer", sdp: "newtracksdp" };
peer.onGotDescription(connectionId, testDesc);
await waitFor(() => answer != null);
expect(answer.connectionId).toBe(connectionId);
});
test(`don't fire sendanswer when on got offer description in impolite that have offer`, async () => {
let offer;
let answer;
const config = getRTCConfiguration();
const peer = new Peer(connectionId, false, config);
expect(peer).not.toBeNull();
peer.addEventListener('sendoffer', (e) => offer = e.detail);
peer.addEventListener('sendanswer', (e) => answer = e.detail);
const track = { id: getUniqueId(), kind: "audio" };
peer.addTrack(connectionId, track);
await waitFor(() => offer != null);
expect(offer.connectionId).toBe(connectionId);
const testDesc = { type: "offer", sdp: "newtracksdp" };
peer.onGotDescription(connectionId, testDesc);
await sleep(100);
expect(answer).toBeUndefined();
});
test(`fire nagotiated when on got answer description that have offer`, async () => {
let offer;
let negotiated = false;
const config = getRTCConfiguration();
const peer = new Peer(connectionId, true, config);
expect(peer).not.toBeNull();
peer.addEventListener('sendoffer', (e) => offer = e.detail);
peer.pc.addEventListener('negotiated', () => negotiated = true);
const track = { id: getUniqueId(), kind: "audio" };
peer.addTrack(connectionId, track);
await waitFor(() => offer != null);
expect(offer.connectionId).toBe(connectionId);
const answerDesc = { type: "answer", sdp: "newtracksdp" };
peer.onGotDescription(connectionId, answerDesc);
await waitFor(() => negotiated);
expect(negotiated).toBeTruthy();
});
test(`fire sendcandidate when on addTransceiver`, async () => {
let candidate;
const config = getRTCConfiguration();
const peer = new Peer(connectionId, true, config);
expect(peer).not.toBeNull();
peer.addEventListener('sendcandidate', (e) => candidate = e.detail);
peer.addTransceiver(connectionId, { id: getUniqueId(), kind: "video" });
await waitFor(() => candidate != null);
expect(candidate.connectionId).toBe(connectionId);
});
test(`accept candidate when on got candidate that have remote description`, async () => {
let answer;
const config = getRTCConfiguration();
const peer = new Peer(connectionId, false, config);
expect(peer).not.toBeNull();
peer.addEventListener('sendanswer', (e) => answer = e.detail);
const testDesc = { type: "offer", sdp: "newtracksdp" };
peer.onGotDescription(connectionId, testDesc);
await waitFor(() => answer != null);
expect(answer.connectionId).toBe(connectionId);
const testCandidate = { candidate: getUniqueId(), sdpMLineIndex: 0, sdpMid: 0 };
peer.onGotCandidate(connectionId, testCandidate);
await waitFor(() => peer.pc.candidates.length > 0);
expect(peer.pc.candidates.length).toBeGreaterThan(0);
});
test(`don't accept candidate when on got candidate that don't have remote description`, async () => {
const config = getRTCConfiguration();
const peer = new Peer(connectionId, false, config);
expect(peer).not.toBeNull();
const testCandidate = { candidate: getUniqueId(), sdpMLineIndex: 0, sdpMid: 0 };
peer.onGotCandidate(connectionId, testCandidate);
await sleep(100);
expect(peer.pc.candidates.length).toBe(0);
});
});

View File

@@ -0,0 +1,45 @@
import {
LetterBoxType,
PointerCorrector
} from "../../src/input/pointercorrect.js";
import {DOMRect} from "../helpers/domrect.js";
import {DOMHTMLVideoElement} from "../helpers/domvideoelement.js";
describe(`PointerCorrector.map`, () => {
test('letterboxType', () => {
const rect = new DOMRect(10, 10, 200, 200);
const element = new DOMHTMLVideoElement(rect);
let corrector = new PointerCorrector(50, 100, element);
expect(corrector.letterBoxType).toBe(LetterBoxType.Vertical);
corrector.reset(100, 50, element);
expect(corrector.letterBoxType).toBe(LetterBoxType.Horizontal);
});
test('letterboxSize', () => {
const rect = new DOMRect(0, 0, 100, 100);
const element = new DOMHTMLVideoElement(rect);
let corrector = new PointerCorrector(50, 100, element);
expect(corrector.letterBoxSize).toBe(25);
});
test('contentRect', () => {
const rect = new DOMRect(0, 0, 100, 100);
const element = new DOMHTMLVideoElement(rect);
let corrector = new PointerCorrector(50, 100, element);
expect(corrector.contentRect.x).toBe(25);
expect(corrector.contentRect.y).toBe(0);
expect(corrector.contentRect.width).toBe(50);
expect(corrector.contentRect.height).toBe(100);
});
test('mapping', () => {
const rect = new DOMRect(10, 10, 200, 200);
const element = new DOMHTMLVideoElement(rect);
const videoWidth = 100;
const videoHeight = 100;
let corrector = new PointerCorrector(videoWidth, videoHeight, element);
const position = [10, 10];
const newPosition = corrector.map(position);
expect(newPosition[0]).toBe(0);
expect(newPosition[1]).toBe(100);
});
});

View File

@@ -0,0 +1,187 @@
import { MockSignaling, reset } from "../mocks/mocksignaling.js";
import { waitFor, getUniqueId, getRTCConfiguration } from "../helpers/testutils.js";
import { RenderStreaming } from "../../src/core/renderstreaming.js";
describe.each([
{ mode: "private" },
{ mode: "public" }
])('renderstreaming test', ({ mode }) => {
const connectionId1 = "12345";
test(`createConnection in ${mode} mode`, async () => {
reset(mode == "private");
const config = getRTCConfiguration();
const renderstreaming = new RenderStreaming(new MockSignaling(), config);
await renderstreaming.start();
let isConnect = false;
renderstreaming.onConnect = () => isConnect = true;
await renderstreaming.createConnection(connectionId1);
await waitFor(() => isConnect);
expect(isConnect).toBe(true);
await renderstreaming.stop();
});
test(`addTrack in ${mode} mode`, async () => {
reset(mode == "private");
const config = getRTCConfiguration();
const renderstreaming = new RenderStreaming(new MockSignaling(), config);
await renderstreaming.start();
let isConnect = false;
renderstreaming.onConnect = () => isConnect = true;
await renderstreaming.createConnection(connectionId1);
await waitFor(() => isConnect);
expect(isConnect).toBe(true);
expect(renderstreaming.getTransceivers(connectionId1).length).toBe(0);
const track = { id: getUniqueId(), kind: "audio" };
renderstreaming.addTrack(track);
expect(renderstreaming.getTransceivers(connectionId1).length).toBe(1);
let isDisconnect = false;
renderstreaming.onDisconnect = () => isDisconnect = true;
await renderstreaming.deleteConnection();
await waitFor(() => isDisconnect);
expect(isDisconnect).toBe(true);
await renderstreaming.stop();
});
test(`createChannel in ${mode} mode`, async () => {
reset(mode == "private");
const config = getRTCConfiguration();
const renderstreaming = new RenderStreaming(new MockSignaling(), config);
await renderstreaming.start();
let isConnect = false;
renderstreaming.onConnect = () => isConnect = true;
await renderstreaming.createConnection(connectionId1);
await waitFor(() => isConnect);
expect(isConnect).toBe(true);
expect(renderstreaming.getTransceivers(connectionId1).length).toBe(0);
const label = "testlabel";
const channel = renderstreaming.createDataChannel(label);
expect(channel.label).toBe(label);
let isDisconnect = false;
renderstreaming.onDisconnect = () => isDisconnect = true;
await renderstreaming.deleteConnection();
await waitFor(() => isDisconnect);
expect(isDisconnect).toBe(true);
await renderstreaming.stop();
});
test(`onTrackEvent in ${mode} mode`, async () => {
reset(mode == "private");
const config = getRTCConfiguration();
const renderstreaming1 = new RenderStreaming(new MockSignaling(), config);
const renderstreaming2 = new RenderStreaming(new MockSignaling(), config);
await renderstreaming1.start();
await renderstreaming2.start();
let isConnect1 = false;
renderstreaming1.onConnect = () => isConnect1 = true;
let isConnect2 = false;
renderstreaming2.onConnect = () => isConnect2 = true;
await renderstreaming1.createConnection(connectionId1);
await renderstreaming2.createConnection(connectionId1);
await waitFor(() => isConnect1 && isConnect2);
expect(isConnect1).toBe(true);
expect(isConnect2).toBe(true);
let isGotOffer1 = false;
let isOnTrack1 = false;
let isGotAnswer2 = false;
renderstreaming1.onGotOffer = () => { isGotOffer1 = true; };
renderstreaming1.onTrackEvent = () => { isOnTrack1 = true; };
renderstreaming2.onGotAnswer = () => { isGotAnswer2 = true; };
expect(renderstreaming1.getTransceivers(connectionId1).length).toBe(0);
const track = { id: getUniqueId(), kind: "audio" };
renderstreaming2.addTrack(track);
expect(renderstreaming2.getTransceivers(connectionId1).length).toBe(1);
await waitFor(() => isGotOffer1);
expect(isGotOffer1).toBe(true);
await waitFor(() => isOnTrack1);
expect(isOnTrack1).toBe(true);
expect(renderstreaming1.getTransceivers(connectionId1).length).toBe(1);
await waitFor(() => isGotAnswer2);
expect(isGotAnswer2).toBe(true);
let isDisconnect1 = false;
renderstreaming1.onDisconnect = () => isDisconnect1 = true;
let isDisconnect2 = false;
renderstreaming2.onDisconnect = () => isDisconnect2 = true;
await renderstreaming1.deleteConnection();
await renderstreaming2.deleteConnection();
await waitFor(() => isDisconnect1 && isDisconnect2);
expect(isDisconnect1).toBe(true);
expect(isDisconnect2).toBe(true);
await renderstreaming1.stop();
await renderstreaming2.stop();
});
test(`onAddDataChannel in ${mode} mode`, async () => {
reset(mode == "private");
const config = getRTCConfiguration();
const renderstreaming1 = new RenderStreaming(new MockSignaling(), config);
const renderstreaming2 = new RenderStreaming(new MockSignaling(), config);
await renderstreaming1.start();
await renderstreaming2.start();
let isConnect1 = false;
renderstreaming1.onConnect = () => isConnect1 = true;
let isConnect2 = false;
renderstreaming2.onConnect = () => isConnect2 = true;
await renderstreaming1.createConnection(connectionId1);
await renderstreaming2.createConnection(connectionId1);
await waitFor(() => isConnect1 && isConnect2);
expect(isConnect1).toBe(true);
expect(isConnect2).toBe(true);
let isGotOffer1 = false;
let isAddChannel1 = false;
let isGotAnswer2 = false;
renderstreaming1.onGotOffer = () => { isGotOffer1 = true; };
renderstreaming1.onAddChannel = () => { isAddChannel1 = true; };
renderstreaming2.onGotAnswer = () => { isGotAnswer2 = true; };
renderstreaming2.createDataChannel("testchannel");
await waitFor(() => isGotOffer1);
expect(isGotOffer1).toBe(true);
await waitFor(() => isAddChannel1);
expect(isAddChannel1).toBe(true);
await waitFor(() => isGotAnswer2);
expect(isGotAnswer2).toBe(true);
let isDisconnect1 = false;
renderstreaming1.onDisconnect = () => isDisconnect1 = true;
let isDisconnect2 = false;
renderstreaming2.onDisconnect = () => isDisconnect2 = true;
await renderstreaming1.deleteConnection();
await renderstreaming2.deleteConnection();
await waitFor(() => isDisconnect1 && isDisconnect2);
expect(isDisconnect1).toBe(true);
expect(isDisconnect2).toBe(true);
await renderstreaming1.stop();
await renderstreaming2.stop();
});
});

View File

@@ -0,0 +1,143 @@
import {
InputRemoting,
} from "../../src/input/inputremoting.js";
import {
Sender,
Observer
} from "../../src/core/sender.js";
import {jest} from '@jest/globals';
import {DOMRect} from "../helpers/domrect.js";
// mock
class RTCDataChannel {
get readyState() {
return "open";
}
/* eslint-disable no-unused-vars */
send(message) {
}
}
describe(`Sender`, () => {
let inputRemoting = null;
let sender = null;
let observer = null;
let events = {};
let dc = null;
beforeEach(async () => {
// Empty our events before each test case
events = {};
// Define the addEventListener method with a Jest mock function
document.addEventListener = jest.fn((event, callback) => {
events[event] = callback;
});
document.removeEventListener = jest.fn((event, callback) => {
delete events[event];
});
document.getBoundingClientRect = function(){ return new DOMRect(0,0,0,0); };
sender = new Sender(document);
inputRemoting = new InputRemoting(sender);
dc = new RTCDataChannel();
observer = new Observer(dc);
});
test('devices', () => {
sender.addMouse();
expect(sender.devices.length).toBe(1);
sender.addKeyboard();
expect(sender.devices.length).toBe(2);
});
test('send messages while called startSending', () => {
jest.spyOn(dc, 'send');
sender.addMouse();
sender.addKeyboard();
inputRemoting.subscribe(observer);
inputRemoting.startSending();
expect(dc.send).toHaveBeenCalled();
});
describe('mouse', () => {
test('click', () => {
jest.spyOn(dc, 'send');
sender.addMouse();
inputRemoting.subscribe(observer);
inputRemoting.startSending();
events.click(
new MouseEvent('click', { buttons:1, clientX:0, clientY:0} ));
expect(dc.send).toHaveBeenCalledWith(expect.any(ArrayBuffer));
});
test('mousemove', () => {
jest.spyOn(dc, 'send');
sender.addMouse();
inputRemoting.subscribe(observer);
inputRemoting.startSending();
events.mousemove(
new MouseEvent('mousemove', { buttons:1, deltaX:0, deltaY:0 }));
expect(dc.send).toHaveBeenCalledWith(expect.any(ArrayBuffer));
});
test('wheel', () => {
jest.spyOn(dc, 'send');
sender.addMouse();
inputRemoting.subscribe(observer);
inputRemoting.startSending();
events.wheel(
new WheelEvent('wheel', { wheelDelta:0, deltaX:0, deltaY:0 }));
expect(dc.send).toHaveBeenCalledWith(expect.any(ArrayBuffer));
});
});
describe('keyboard', () => {
test('keydown', () => {
jest.spyOn(dc, 'send');
sender.addKeyboard();
inputRemoting.subscribe(observer);
inputRemoting.startSending();
events.keydown(
new KeyboardEvent('keydown', { code: 'KeyA' }));
expect(dc.send).toHaveBeenCalledWith(expect.any(ArrayBuffer));
});
test('keydown repeat', () => {
jest.spyOn(dc, 'send');
sender.addKeyboard();
inputRemoting.subscribe(observer);
inputRemoting.startSending();
events.keydown(
new KeyboardEvent('keydown', { code: 'KeyA', repeat: true }));
expect(dc.send).toHaveBeenCalledWith(expect.any(ArrayBuffer));
});
});
describe('touchscreen', () => {
test('touchstart', () => {
jest.spyOn(dc, 'send');
sender.addTouchscreen();
inputRemoting.subscribe(observer);
inputRemoting.startSending();
events.touchstart(
new TouchEvent("touchstart", {
changedTouches: [{ // InputInit
identifier: 0,
target: null,
clientX: 0,
clientY: 0,
screenX: 0,
screenY: 0,
pageX: 0,
pageY: 0,
radiusX: 0,
radiusY: 0,
rotationAngle: 0,
force: 0,
altitudeAngle: 0,
azimuthAngle:0,
touchType: "direct"
}]
}));
expect(dc.send).toHaveBeenCalledWith(expect.any(ArrayBuffer));
});
});
describe('gamepad', () => {
//todo
});
});

View File

@@ -0,0 +1,484 @@
import { jest } from '@jest/globals';
import * as Path from 'path';
import { setup, teardown } from 'jest-dev-server';
import { Signaling, WebSocketSignaling } from "../../src/core/signaling.js";
import { MockSignaling, reset } from "../mocks/mocksignaling.js";
import { waitFor, sleep, serverExeName } from "../helpers/testutils.js";
const portNumber = 8081;
jest.setTimeout(10000);
describe.each([
{ mode: "mock" },
{ mode: "http" },
{ mode: "websocket" },
])('signaling test in public mode', ({ mode }) => {
let signaling1;
let signaling2;
const connectionId1 = "12345";
const connectionId2 = "67890";
const testsdp = "test sdp";
const testcandidate = "test candidate";
beforeAll(async () => {
if (mode == "mock") {
reset(false);
signaling1 = new MockSignaling(1);
signaling2 = new MockSignaling(1);
} else {
const path = Path.resolve(`../bin~/${serverExeName()}`);
let cmd = `${path} -p ${portNumber}`;
if (mode == "http") {
cmd += " -t http";
}
await setup({ command: cmd, port: portNumber, usedPortAction: 'error' });
if (mode == "http") {
signaling1 = new Signaling(1);
signaling2 = new Signaling(1);
}
if (mode == "websocket") {
signaling1 = new WebSocketSignaling(1);
signaling2 = new WebSocketSignaling(1);
}
}
await signaling1.start();
await signaling2.start();
});
afterAll(async () => {
await signaling1.stop();
await signaling2.stop();
signaling1 = null;
signaling2 = null;
if (mode == "mock") {
return;
}
await teardown();
// work around for linux, waitng kill server process
await sleep(1000);
});
test(`onConnect using ${mode}`, async () => {
const signaling1Spy = jest.spyOn(signaling1, 'dispatchEvent');
let connectRes;
let disconnectRes;
signaling1.addEventListener('connect', (e) => connectRes = e.detail);
signaling1.addEventListener('disconnect', (e) => disconnectRes = e.detail);
await signaling1.createConnection(connectionId1);
await waitFor(() => connectRes != null);
expect(connectRes.connectionId).toBe(connectionId1);
expect(connectRes.polite).toBe(true);
await signaling1.deleteConnection(connectionId1);
await waitFor(() => disconnectRes != null);
expect(disconnectRes.connectionId).toBe(connectionId1);
const disconnectCalledCount = signaling1Spy.mock.calls.map(x => x[0].type).filter(x => x == "disconnect").length;
expect(disconnectCalledCount).toBe(1);
signaling1Spy.mockRestore();
});
test(`onOffer using ${mode}`, async () => {
let connectRes1;
let disconnectRes1;
signaling1.addEventListener('connect', (e) => connectRes1 = e.detail);
signaling1.addEventListener('disconnect', (e) => disconnectRes1 = e.detail);
let connectRes2;
let disconnectRes2;
let offerRes2;
signaling2.addEventListener('connect', (e) => connectRes2 = e.detail);
signaling2.addEventListener('disconnect', (e) => disconnectRes2 = e.detail);
signaling2.addEventListener('offer', (e) => offerRes2 = e.detail);
await signaling1.createConnection(connectionId1);
await signaling2.createConnection(connectionId2);
await waitFor(() => connectRes1 != null && connectRes2 != null);
expect(connectRes1.connectionId).toBe(connectionId1);
expect(connectRes2.connectionId).toBe(connectionId2);
await signaling1.sendOffer(connectionId1, testsdp);
await waitFor(() => offerRes2 != null);
expect(offerRes2.connectionId).toBe(connectionId1);
expect(offerRes2.polite).toBe(false);
await signaling1.deleteConnection(connectionId1);
await waitFor(() => disconnectRes1 != null);
expect(disconnectRes1.connectionId).toBe(connectionId1);
await signaling2.deleteConnection(connectionId2);
await waitFor(() => disconnectRes2 != null);
expect(disconnectRes2.connectionId).toBe(connectionId2);
});
test(`onAnswer using ${mode}`, async () => {
let connectRes1;
let disconnectRes1;
let answerRes1;
signaling1.addEventListener('connect', (e) => connectRes1 = e.detail);
signaling1.addEventListener('disconnect', (e) => disconnectRes1 = e.detail);
signaling1.addEventListener('answer', (e) => answerRes1 = e.detail);
let connectRes2;
let disconnectRes2;
let offerRes2;
signaling2.addEventListener('connect', (e) => connectRes2 = e.detail);
signaling2.addEventListener('disconnect', (e) => disconnectRes2 = e.detail);
signaling2.addEventListener('offer', (e) => offerRes2 = e.detail);
await signaling1.createConnection(connectionId1);
await signaling2.createConnection(connectionId2);
await waitFor(() => connectRes1 != null && connectRes2 != null);
await signaling1.sendOffer(connectionId1, testsdp);
await waitFor(() => offerRes2 != null);
expect(offerRes2.connectionId).toBe(connectionId1);
expect(offerRes2.sdp).toBe(testsdp);
signaling2.sendAnswer(connectionId1, testsdp);
await waitFor(() => answerRes1 != null);
expect(answerRes1.connectionId).toBe(connectionId1);
expect(answerRes1.sdp).toBe(testsdp);
await signaling1.deleteConnection(connectionId1);
await waitFor(() => disconnectRes1 != null);
await signaling2.deleteConnection(connectionId2);
await waitFor(() => disconnectRes2 != null);
});
test(`onCandidate using ${mode}`, async () => {
let connectRes1;
let disconnectRes1;
let answerRes1;
let candidateRes1;
signaling1.addEventListener('connect', (e) => connectRes1 = e.detail);
signaling1.addEventListener('disconnect', (e) => disconnectRes1 = e.detail);
signaling1.addEventListener('answer', (e) => answerRes1 = e.detail);
signaling1.addEventListener('candidate', (e) => candidateRes1 = e.detail);
let connectRes2;
let disconnectRes2;
let offerRes2;
let candidateRes2;
signaling2.addEventListener('connect', (e) => connectRes2 = e.detail);
signaling2.addEventListener('disconnect', (e) => disconnectRes2 = e.detail);
signaling2.addEventListener('offer', (e) => offerRes2 = e.detail);
signaling2.addEventListener('candidate', (e) => candidateRes2 = e.detail);
await signaling1.createConnection(connectionId1);
await signaling2.createConnection(connectionId2);
await waitFor(() => connectRes1 != null && connectRes2 != null);
await signaling1.sendOffer(connectionId1, testsdp);
await waitFor(() => offerRes2 != null);
expect(offerRes2.connectionId).toBe(connectionId1);
expect(offerRes2.sdp).toBe(testsdp);
signaling2.sendAnswer(connectionId1, testsdp);
await waitFor(() => answerRes1 != null);
expect(answerRes1.connectionId).toBe(connectionId1);
expect(answerRes1.sdp).toBe(testsdp);
await signaling2.sendCandidate(connectionId1, testcandidate, 1, 1);
await waitFor(() => candidateRes1 != null);
expect(candidateRes1.connectionId).toBe(connectionId1);
expect(candidateRes1.candidate).toBe(testcandidate);
expect(candidateRes1.sdpMid).toBe(1);
expect(candidateRes1.sdpMLineIndex).toBe(1);
await signaling1.sendCandidate(connectionId1, testcandidate, 1, 1);
await waitFor(() => candidateRes2 != null);
expect(candidateRes2.connectionId).toBe(connectionId1);
expect(candidateRes2.candidate).toBe(testcandidate);
expect(candidateRes2.sdpMid).toBe(1);
expect(candidateRes2.sdpMLineIndex).toBe(1);
await signaling1.deleteConnection(connectionId1);
await waitFor(() => disconnectRes1 != null);
await signaling2.deleteConnection(connectionId2);
await waitFor(() => disconnectRes2 != null);
});
});
describe.each([
{ mode: "mock" },
{ mode: "http" },
{ mode: "websocket" },
])('signaling test in private mode', ({ mode }) => {
let signaling1;
let signaling2;
const connectionId = "12345";
const testsdp = "test sdp";
const testcandidate = "test candidate";
beforeAll(async () => {
if (mode == "mock") {
reset(true);
signaling1 = new MockSignaling(1);
signaling2 = new MockSignaling(1);
return;
}
const path = Path.resolve(`../bin~/${serverExeName()}`);
let cmd = `${path} -p ${portNumber} -m private`;
if (mode == "http") {
cmd += " -t http";
}
await setup({ command: cmd, port: portNumber, usedPortAction: 'error' });
if (mode == "http") {
signaling1 = new Signaling(1);
signaling2 = new Signaling(1);
}
if (mode == "websocket") {
signaling1 = new WebSocketSignaling(1);
signaling2 = new WebSocketSignaling(1);
}
await signaling1.start();
await signaling2.start();
});
afterAll(async () => {
await signaling1.stop();
await signaling2.stop();
signaling1 = null;
signaling2 = null;
if (mode == "mock") {
return;
}
await teardown();
// work around for linux, waitng kill server process
await sleep(1000);
});
test(`onConnect using ${mode}`, async () => {
let connectRes1;
let disconnectRes1;
signaling1.addEventListener('connect', (e) => connectRes1 = e.detail);
signaling1.addEventListener('disconnect', (e) => disconnectRes1 = e.detail);
let connectRes2;
let disconnectRes2;
signaling2.addEventListener('connect', (e) => connectRes2 = e.detail);
signaling2.addEventListener('disconnect', (e) => disconnectRes2 = e.detail);
await signaling1.createConnection(connectionId);
await waitFor(() => connectRes1 != null);
expect(connectRes1.connectionId).toBe(connectionId);
expect(connectRes1.polite).toBe(false);
await signaling2.createConnection(connectionId);
await waitFor(() => connectRes2 != null);
expect(connectRes2.connectionId).toBe(connectionId);
expect(connectRes2.polite).toBe(true);
await sleep(signaling1.interval * 2);
await signaling1.deleteConnection(connectionId);
await waitFor(() => disconnectRes1 != null && disconnectRes2 != null);
expect(disconnectRes1.connectionId).toBe(connectionId);
expect(disconnectRes2.connectionId).toBe(connectionId);
disconnectRes2 = null;
await signaling2.deleteConnection(connectionId);
await waitFor(() => disconnectRes2 != null);
});
test(`onOffer using ${mode}`, async () => {
let connectRes1;
let disconnectRes1;
signaling1.addEventListener('connect', (e) => connectRes1 = e.detail);
signaling1.addEventListener('disconnect', (e) => disconnectRes1 = e.detail);
let connectRes2;
let disconnectRes2;
let offerRes2;
signaling2.addEventListener('connect', (e) => connectRes2 = e.detail);
signaling2.addEventListener('disconnect', (e) => disconnectRes2 = e.detail);
signaling2.addEventListener('offer', (e) => offerRes2 = e.detail);
await signaling1.createConnection(connectionId);
await waitFor(() => connectRes1 != null);
expect(connectRes1.connectionId).toBe(connectionId);
signaling1.sendOffer(connectionId, testsdp);
await sleep(signaling1.interval * 2);
// Do not receive offer other signaling if not connected same sendoffer connectionId in private mode
expect(offerRes2).toBeUndefined();
await signaling2.createConnection(connectionId);
await waitFor(() => connectRes2 != null);
expect(connectRes2.connectionId).toBe(connectionId);
await signaling1.sendOffer(connectionId, testsdp);
await waitFor(() => offerRes2 != null);
expect(offerRes2.connectionId).toBe(connectionId);
expect(offerRes2.polite).toBe(true);
await signaling1.deleteConnection(connectionId);
await waitFor(() => disconnectRes1 != null && disconnectRes2 != null);
expect(disconnectRes1.connectionId).toBe(connectionId);
expect(disconnectRes2.connectionId).toBe(connectionId);
disconnectRes2 = null;
await signaling2.deleteConnection(connectionId);
await waitFor(() => disconnectRes2 != null);
});
test(`onAnswer using ${mode}`, async () => {
let connectRes1;
let disconnectRes1;
let answerRes1;
signaling1.addEventListener('connect', (e) => connectRes1 = e.detail);
signaling1.addEventListener('disconnect', (e) => disconnectRes1 = e.detail);
signaling1.addEventListener('answer', (e) => answerRes1 = e.detail);
let connectRes2;
let disconnectRes2;
let offerRes2;
signaling2.addEventListener('connect', (e) => connectRes2 = e.detail);
signaling2.addEventListener('disconnect', (e) => disconnectRes2 = e.detail);
signaling2.addEventListener('offer', (e) => offerRes2 = e.detail);
await signaling1.createConnection(connectionId);
await signaling2.createConnection(connectionId);
await waitFor(() => connectRes1 != null && connectRes2 != null);
await signaling1.sendOffer(connectionId, testsdp);
await waitFor(() => offerRes2 != null);
expect(offerRes2.connectionId).toBe(connectionId);
expect(offerRes2.sdp).toBe(testsdp);
await signaling2.sendAnswer(connectionId, testsdp);
await waitFor(() => answerRes1 != null);
expect(answerRes1.connectionId).toBe(connectionId);
expect(answerRes1.sdp).toBe(testsdp);
await signaling1.deleteConnection(connectionId);
await waitFor(() => disconnectRes1 != null && disconnectRes2 != null);
expect(disconnectRes1.connectionId).toBe(connectionId);
expect(disconnectRes2.connectionId).toBe(connectionId);
disconnectRes2 = null;
await signaling2.deleteConnection(connectionId);
await waitFor(() => disconnectRes2 != null);
});
test(`onCandidate using ${mode}`, async () => {
let connectRes1;
let disconnectRes1;
let answerRes1;
let candidateRes1;
signaling1.addEventListener('connect', (e) => connectRes1 = e.detail);
signaling1.addEventListener('disconnect', (e) => disconnectRes1 = e.detail);
signaling1.addEventListener('answer', (e) => answerRes1 = e.detail);
signaling1.addEventListener('candidate', (e) => candidateRes1 = e.detail);
let connectRes2;
let disconnectRes2;
let offerRes2;
let candidateRes2;
signaling2.addEventListener('connect', (e) => connectRes2 = e.detail);
signaling2.addEventListener('disconnect', (e) => disconnectRes2 = e.detail);
signaling2.addEventListener('offer', (e) => offerRes2 = e.detail);
signaling2.addEventListener('candidate', (e) => candidateRes2 = e.detail);
await signaling1.createConnection(connectionId);
await signaling2.createConnection(connectionId);
await waitFor(() => connectRes1 != null && connectRes2 != null);
await signaling1.sendOffer(connectionId, testsdp);
await waitFor(() => offerRes2 != null);
expect(offerRes2.connectionId).toBe(connectionId);
expect(offerRes2.sdp).toBe(testsdp);
await signaling2.sendAnswer(connectionId, testsdp);
await waitFor(() => answerRes1 != null);
expect(answerRes1.connectionId).toBe(connectionId);
expect(answerRes1.sdp).toBe(testsdp);
await signaling2.sendCandidate(connectionId, testcandidate, 1, 1);
await waitFor(() => candidateRes1 != null);
expect(candidateRes1.connectionId).toBe(connectionId);
expect(candidateRes1.candidate).toBe(testcandidate);
expect(candidateRes1.sdpMLineIndex).toBe(1);
expect(candidateRes1.sdpMid).toBe(1);
await signaling1.sendCandidate(connectionId, testcandidate, 1, 1);
await waitFor(() => candidateRes2 != null);
expect(candidateRes2.connectionId).toBe(connectionId);
expect(candidateRes2.candidate).toBe(testcandidate);
expect(candidateRes2.sdpMLineIndex).toBe(1);
expect(candidateRes2.sdpMid).toBe(1);
await signaling1.deleteConnection(connectionId);
await waitFor(() => disconnectRes1 != null && disconnectRes2 != null);
expect(disconnectRes1.connectionId).toBe(connectionId);
expect(disconnectRes2.connectionId).toBe(connectionId);
disconnectRes2 = null;
await signaling2.deleteConnection(connectionId);
await waitFor(() => disconnectRes2 != null);
});
test(`notReceiveOwnOfferAnswer using ${mode}`, async () => {
let connectRes1;
let disconnectRes1;
let offerRes1;
let answerRes1;
signaling1.addEventListener('connect', (e) => connectRes1 = e.detail);
signaling1.addEventListener('disconnect', (e) => disconnectRes1 = e.detail);
let connectRes2;
let disconnectRes2;
let offerRes2;
let answerRes2;
signaling2.addEventListener('connect', (e) => connectRes2 = e.detail);
signaling2.addEventListener('disconnect', (e) => disconnectRes2 = e.detail);
await signaling1.createConnection(connectionId);
await signaling2.createConnection(connectionId);
await waitFor(() => connectRes1 != null && connectRes2 != null);
signaling1.addEventListener('offer', (e) => offerRes1 = e.detail);
signaling2.addEventListener('offer', (e) => offerRes2 = e.detail);
await signaling1.sendOffer(connectionId, testsdp);
await waitFor(() => offerRes2 != null);
await sleep(signaling1.interval * 2);
expect(offerRes1).toBeUndefined();
expect(offerRes2).not.toBeUndefined();
expect(offerRes2.connectionId).toBe(connectionId);
expect(offerRes2.sdp).toBe(testsdp);
signaling1.addEventListener('answer', (e) => answerRes1 = e.detail);
signaling2.addEventListener('answer', (e) => answerRes2 = e.detail);
await signaling2.sendAnswer(connectionId, testsdp);
await waitFor(() => answerRes1 != null);
await sleep(signaling2.interval * 2);
expect(answerRes1).not.toBeUndefined();
expect(answerRes1.connectionId).toBe(connectionId);
expect(answerRes1.sdp).toBe(testsdp);
expect(answerRes2).toBeUndefined();
await signaling1.deleteConnection(connectionId);
await waitFor(() => disconnectRes1 != null && disconnectRes2 != null);
expect(disconnectRes1.connectionId).toBe(connectionId);
expect(disconnectRes2.connectionId).toBe(connectionId);
disconnectRes2 = null;
await signaling2.deleteConnection(connectionId);
await waitFor(() => disconnectRes2 != null);
});
});