<|start_filename|>test/node/conversation.js<|end_filename|>
'use strict';

const assert = require('assert');
const Circuit = require('../../circuit-node');
const prep = require('../preparation');
Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);

let client;
let conversation;
describe('Conversation Tests', () => {
    before(async () => {
        client = prep.client;
        // get the conversation by its Id to get the latest modification times
        conversation = await client.getConversationById(prep.conversation.convId); 
    });

    it('function: getConversations', async () => {
        const res = await client.getConversations();
        assert(res && res.some(conv => conv.convId === conversation.convId));
    });

    // it('should get a number of participants for conversation', async () => {
    //     const res = await client.getConversations({
    //         numberOfParticipants: 1
    //     });
    //     assert(res && res.some(conv => conv.convId === conversation.convId) && res.every(conv => conv.participants.length <= 1));
    // });

    it('function: getConversations. with {numberOfConversations: 3}', async () => {
        const res = await client.getConversations({
            numberOfConversations: 3
        });
        assert(res && res.length === 3);
    });

    it('function: getConversations. with {direction: AFTER, timestamp: conversation.modificationTime - 1}', async () => {
        const res = await client.getConversations({
            direction: Circuit.Constants.SearchDirection.AFTER,
            timestamp: conversation.modificationTime - 1
        });
        assert(res && res.some(conv => conv.convId === conversation.convId));
    });

    it('function: getConversations. with {direction: AFTER, timestamp: conversation.modificationTime + 1}', async () => {
        const res = await client.getConversations({
            direction: Circuit.Constants.SearchDirection.AFTER,
            timestamp: conversation.lastItemModificationTime + 1
        });
        assert(res && !res.some(conv => conv.convId === conversation.convId));
    });

    it('function: getConversations. with {direction: BEFORE, timestamp: conversation.lastItemModificationTime + 1}', async () => {
        const res = await client.getConversations({
            direction: Circuit.Constants.SearchDirection.BEFORE,
            timestamp: conversation.lastItemModificationTime + 1
        });
        assert(res && res.some(conv => conv.convId === conversation.convId));
    });

    it('function: getConversations. with {direction: BEFORE, timestamp: conversation.lastItemModificationTime - 1}', async () => {
        const res = await client.getConversations({
            direction: Circuit.Constants.SearchDirection.BEFORE,
            timestamp: conversation.creationTime - 1
        });
        assert(res && !res.some(conv => conv.convId === conversation.convId));
    });
});

<|start_filename|>test/browser/call-device.js<|end_filename|>
'use strict';

import { PeerUser } from '../peer-user.js';
import { expectEvents, sleep } from '../helper.js';
import config from './config.js'

const assert = chai.assert;
let client;
let peerUser;
let call;
let user;
let stream;
describe('Call Devices', async function() {
    this.timeout(300000);

    before(async function() {
        Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);
        client = new Circuit.Client(config.config);
        const res = await Promise.all([PeerUser.create(), client.logon(config.credentials)]);
        peerUser = res[0];
        user = res[1];
        const conversation = await client.createGroupConversation([peerUser.userId], 'SDK Test: Call Devices');
        call = await client.startConference(conversation.convId, {audio: true, video: false});
        await expectEvents(client, [{
            type: 'callStatus',
            predicate: evt => evt.call.state === Circuit.Enums.CallStateName.Initiated
        }, {
            type: 'callStatus',
            predicate: evt => evt.call.state === Circuit.Enums.CallStateName.Waiting
        }]);
        await sleep(5000); // wait to make sure the call is ready to be joined
        await Promise.all([
            peerUser.exec('joinConference', call.callId, {audio: true, video: false}),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'participantJoined'
            }])
        ]);
        call = await client.findCall(call.callId);
    });

    after(async function() {
        await Promise.all([peerUser.destroy(), client.logout()]);
    });

    afterEach(async function() {
        client.removeAllListeners();
    });

    it('function: isCompatible', async () => {
        const res = await Circuit.isCompatible();
        assert(res);
    });

    it('function: supportedFeatures', async () => {
        const res = await Circuit.supportedFeatures();
        assert(res.text && res.rtc);
    });

    it('function: getMaxVideoResolution', async () => {
        const mediaDevices = await navigator.mediaDevices.enumerateDevices();
        const videoInputDevice = mediaDevices.find(d => d.kind === 'videoinput');
        if (!videoInputDevice) {
            console.log('No video input device found.');
            assert(true);
            return;
        }
        const res = await client.getMaxVideoResolution(videoInputDevice.deviceId);
        assert(Object.keys(Circuit.Enums.VideoResolution).some(resolution => resolution === res));
    });

    it('function: getDevices', async () => {
        const res = await client.getDevices();
        assert(res.some(dev => dev.clientId === user.clientId));
    });

    it('function: getAudioVideoStats', async () => {
        let flag = false; // flag used to check if one of the audio/video stats is a stream
        const res = await client.getAudioVideoStats();
        res.forEach(stat => stat.type === 'stream' ? flag = true : false);
        assert(flag);
    });    
    
    it('function: getLocalAudioVideoStream', async () => {
        stream = await client.getLocalAudioVideoStream();
        assert(stream);
    });
    
    it('function: setAudioVideoStream', async () => {
        await client.setAudioVideoStream(call.callId, stream);
    });

    it('function: toggleRemoteAudio [OFF], raises event: callStatus with reason: remoteStreamUpdated', async () => {
        await Promise.all([
            client.toggleRemoteAudio(call.callId),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'remoteStreamUpdated' && evt.call.callId === call.callId && evt.call.remoteAudioDisabled
            }])
        ]);
    });

    it('function: toggleRemoteAudio [ON], raises event: callStatus with reason: remoteStreamUpdated', async () => {
        await Promise.all([
            client.toggleRemoteAudio(call.callId),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'remoteStreamUpdated' && evt.call.callId === call.callId && !evt.call.remoteAudioDisabled
            }])
        ]);
    });

    it('function: getRemoteStreams', async () => {
        const res = await client.getRemoteStreams(call.callId);
        assert(res);
    });

    it('function: toggleRemoteVideo [OFF], raises event: callStatus with reason: sdpConnected', async () => {
        await Promise.all([
            client.toggleRemoteVideo(call.callId),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'sdpConnected' && evt.call.callId === call.callId && evt.call.remoteVideoDisabled
            }])
        ]);
    });

    it('function: toggleRemoteVideo [ON], raises event: callStatus with reason: sdpConnected', async () => {
        await Promise.all([
            client.toggleRemoteVideo(call.callId),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'sdpConnected' && evt.call.callId === call.callId && !evt.call.remoteVideoDisabled
            }])
        ]);
    });

    it('function: setMediaDevices, raises event: callStatus with reason: callStateChanged', async () => {
        const  mediaDevices = await navigator.mediaDevices.enumerateDevices();
        const videoInputDevice = mediaDevices.find(d => d.kind === 'videoinput');
        const audioInputDevice = mediaDevices.find(d => d.kind === 'audioinput');
        const audioOutputDevice = mediaDevices.find(d => d.kind === 'audiooutput');
        let devices = {
            recording: audioInputDevice.deviceId,
            playback: audioOutputDevice.deviceId,
            video: videoInputDevice.deviceId,
            ringiing: audioOutputDevice.deviceId
        }
        await Promise.all([
            client.setMediaDevices(devices),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'callStateChanged' && evt.call.callId === call.callId
            }])
        ]);
    });
});

<|start_filename|>test/helper.js<|end_filename|>

function remove(array, element) {
  const index = array.indexOf(element);
  array.splice(index, 1);
}

/**
 * Delay processing. Returns a promise.
 * @param {Number} ms Time to sleep in ms
 */
export function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

/**
 * expectEvents
 * @param {Object} client Circuit client instance
 * @param {Object[]} events Array with event type and optional predicate function
 * @returns {Promise}
 */
export function expectEvents(client, events) {
  return new Promise((resolve, reject) => {
    const evts = events.map(e =>
      typeof e === 'string' ? { type: e, predicate: () => { return true; } } : e
    )
    const fn = evt => {
      const matchingEvents = evts.filter(e => e.type === evt.type);
      if (matchingEvents.length) {
        const resolvedEvents = matchingEvents.filter(me => !me.predicate || me.predicate(evt));
        resolvedEvents.forEach(re => remove(evts, re));
        if (!evts.length) {
          events.forEach(event => client.removeEventListener(event.type, fn))
          resolve(evt);
        }
      }
    }
    evts.forEach(event => client.addEventListener(event.type, fn));
  });
}

export function logEvents(client, events) {
  events.forEach(e =>
    client.addEventListener(e, evt => {
      let msg = `Received ${e} event.`;
      evt.reason && (msg += ` Reason: ${evt.reason}.`);
      evt.call && (msg += ` State: ${evt.call.state}.`);
      console.log(msg);
    })
  );
}

export function updateRemoteVideos(client) {
  client.addEventListener('callStatus', evt => {
    evt.call.participants.forEach((p, i) => {
      const el = document.querySelector(`#peerVideo${i}`);
      if (el && el.srcObject !== evt.call.participants[i].videoStream) {
        el.srcObject = evt.call.participants[i].videoStream || null;
      }
    });
  });
  client.addEventListener('callEnded', evt => {
    evt.call.participants.forEach((p, i) => {
      const el = document.querySelector(`#peerVideo${i}`);
      el && (el.srcObject = null);
    });
  });
}

export function clearAllVideos() {
  for (let node of document.querySelectorAll('video')) {
    node.srcObject = null;
  }
}


<|start_filename|>test/browser/call-search.js<|end_filename|>
'use strict';

import { PeerUser } from '../peer-user.js';
import { expectEvents, sleep } from '../helper.js';
import config from './config.js'

const assert = chai.assert;
let client;
let peerUser;
let call;
describe('Call Search', async function() {
    this.timeout(300000);

    before(async function() {
        Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);
        client = new Circuit.Client(config.config);
        const res = await Promise.all([PeerUser.create(), client.logon(config.credentials)]);
        peerUser = res[0];
        const conversation = await client.createGroupConversation([peerUser.userId], 'SDK Test: Call Search');
        call = await client.startConference(conversation.convId, {audio: true, video: false});
        await expectEvents(client, [{
            type: 'callStatus',
            predicate: evt => evt.call.state === Circuit.Enums.CallStateName.Initiated
        }, {
            type: 'callStatus',
            predicate: evt => evt.call.state === Circuit.Enums.CallStateName.Waiting
        }]);
        await sleep(3000); // wait to make sure the call is ready to be joined
        await Promise.all([
            peerUser.exec('joinConference', call.callId, {audio: true, video: false}),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'callStateChanged' && evt.call.state === Circuit.Enums.CallStateName.Active
            }, {
                type: 'callStatus',
                predicate: evt => evt.reason === 'participantJoined'
            }])
        ]);
        call = await client.findCall(call.callId);
    });

    after(async function() {
        await Promise.all([peerUser.destroy(), client.logout()]);
    });

    afterEach(async function() {
        client.removeAllListeners();
    });

    it('function: getActiveCall', async () => {
        const res = await client.getActiveCall();
        assert(res.callId === call.callId);
    });

    it('function: getCalls', async () => {
        const res = await client.getCalls();
        assert(res && res.some(c => c.callId === call.callId));
    });

    it('function: findCall', async () => {
        const res = await client.findCall(call.callId);
        assert(res.callId === call.callId);
    });

    it('function: getLastRtpStats', async () => {
        await sleep(6000); // add pause to allow time for last stat collection interval of 5 seconds
        const res = await client.getLastRtpStats(call.callId);
        assert(res.some(stat => stat.pcType === 'AUDIO/VIDEO'));
    });
});

<|start_filename|>test/browser/conference.js<|end_filename|>
'use strict';

import { PeerUser } from '../peer-user.js';
import { expectEvents, updateRemoteVideos, sleep } from '../helper.js';
import config from './config.js'

const assert = chai.assert;
let client;
let peerUser1, peerUser2;
let call;
describe('Conference Call', async function() {
    this.timeout(300000);

    before(async function() {
        Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);
        client = new Circuit.Client(config.config);
        const res = await Promise.all([PeerUser.create(), PeerUser.create(), client.logon(config.credentials)]);
        peerUser1 = res[0];
        peerUser2 = res[1];
    });

    after(async function() {
        document.querySelector('#localVideo').srcObject = null;
        await Promise.all([peerUser1.destroy(), peerUser2.destroy(), client.logout()]);
    });

    afterEach(async function() {
        client.removeAllListeners();
    });

    it('functions: [createGroupConversation, startConference], with event: callStatus with states: [Initiated, Waiting]', async () => {
        const conversation = await client.createGroupConversation([peerUser1.userId, peerUser2.userId], 'SDK Test: Conference Call');
        assert(!!conversation, 'createGroupConversation not successful');
        call = await client.startConference(conversation.convId, {audio: false, video: false});
        await expectEvents(client, [{
            type: 'callStatus',
            predicate: evt => evt.call.state === Circuit.Enums.CallStateName.Initiated
        }, {
            type: 'callStatus',
            predicate: evt => evt.call.state === Circuit.Enums.CallStateName.Waiting
        }]);
        document.querySelector('#localVideo').srcObject = call.localVideoStream;
        assert(call.callId);
    });

    it('function: joinConference, with event: callStatus with reaons: [callStateChanged, participantJoined]', async () => {
        await sleep(5000); // wait to make sure the call is ready to be joined
        updateRemoteVideos(client);
        const res = await Promise.all([
            peerUser1.exec('joinConference', call.callId, {audio: false, video: false}),
            peerUser2.exec('joinConference', call.callId, {audio: false, video: false}),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'callStateChanged' && evt.call.state === Circuit.Enums.CallStateName.Active
            }, {
                type: 'callStatus',
                predicate: evt => evt.reason === 'participantJoined'
            }])
        ]);
        assert(res[2].call.callId === call.callId);
    });

    it('function: endConference, with event: callEnded', async () => {
        updateRemoteVideos(client);
        const res = await Promise.all([client.endConference(call.callId), expectEvents(client, ['callEnded'])]);
        assert(res[1].call.callId === call.callId);
    });
});


<|start_filename|>test/peer-user.js<|end_filename|>
/**
 * Proxy class to control peer users
 */
export class PeerUser {
    constructor(user) {
        this.user = user;
    }

    get username() {
        return this.user.emailAddress;
    }

    get emailAddress() {
        return this.user.emailAddress;
    }

    get userId() {
        return this.user.userId;
    }

    get displayName() {
        return this.user.displayName;
    }

    static async create() {
        const user = await window.onPeerUserCmd({name: 'create'});
        return new PeerUser(user);
    }

    async destroy() {
        return await window.onPeerUserCmd({name: 'destroy', username: this.username})
    }

    async exec(name, ...args) {
        return await window.onPeerUserCmd({name: name, args: args, username: this.username})
    }
}

<|start_filename|>test/node/conference-bridge.js<|end_filename|>
'use strict';

const assert = require('assert');
const Circuit = require('../../circuit-node');
const helper = require('./helper');
const prep = require('../preparation');
Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);

let client;
let user;
let client2;
let user2;
let conference;
describe('Conference Bridge', () => {
    before(async () => {
        client = prep.client;
        user = client.loggedOnUser;
        client2 = prep.client2;
        user2 = client2.loggedOnUser;
    });

    it('function: createConferenceBridge, with event: conversationCreated', async () => {
        const topic = `${Date.now()}a`;
        const res = await Promise.all([
            client.createConferenceBridge(topic),
            helper.expectEvents(client, [{
                type: 'conversationCreated',
                predicate: evt => evt.conversation.topic === topic && evt.conversation.creatorId === user.userId
            }])
        ]);
        conference = res[0];
        assert(conference && conference.topic === topic && conference.creatorId === user.userId);
    });
   
    it('function: getConferenceInvitationText', async () => {
        if (!client.getConferenceInvitationText) {
            console.log('API not yet supported');
            assert(true);
            return;
        }
        const res = await client.getConferenceInvitationText(user.locale, conference.convId);
        if (!res || !res.includes(conference.topic)) {
            assert(false);
        }
    });

    it('function: addParticipant, with event: conversationUpdated', async () => {
        const res = await Promise.all([
            client.addParticipant(conference.convId, user2.userId),
            helper.expectEvents(client, [{
                type: 'conversationUpdated',
                predicate: evt => evt.conversation.convId === conference.convId && evt.conversation.participants.includes(user2.userId)
            }])
        ]);
        if (res[0].convId === conference.convId && res[0].participants.includes(user2.userId)) {  
            conference = res[0];
            assert(true);
        } else {
            assert(false);
        }
    });

    it('function: getConversationsByIds', async () => {
        const res = await client.getConversationsByIds([conference.convId]);
        const conf = res[0];
        assert(conf && conf.convId === conference.convId && conf.topic === conference.topic);
    });
});

<|start_filename|>test/node/user-settings.js<|end_filename|>
'use strict';

const assert = require('assert');
const Circuit = require('../../circuit-node');
const helper = require('./helper');
const prep = require('../preparation');
Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);

let client;
let settings;
describe('User Settings', () => {
    before(async () => {
        client = prep.client;
    });

    it('function: getUserSettings', async () => {
        settings = await client.getUserSettings();
        assert(settings);
    });

    it('functions: [setUserSettings, getUserSettings], with event: userSettingsChanged', async () => {    
        const shareLocation = !!!settings.shareLocation;
        const newSettings = {
            shareLocation: shareLocation
        }
        await Promise.all([
            client.setUserSettings(newSettings),
            helper.expectEvents(client, [{
                type: 'userSettingsChanged',
                predicate: evt => evt.userSettings.shareLocation === shareLocation
            }])
        ]);
        settings = await client.getUserSettings();
        assert(settings && settings.shareLocation === shareLocation);
    });
});

<|start_filename|>test/node/conversation-topics.js<|end_filename|>
'use strict';

const assert = require('assert');
const Circuit = require('../../circuit-node');
const prep = require('../preparation');
Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);

let client;
let conversation;
let topic1;
let topic2;
let topic2Item;
let topic3;
describe('Conversation Topic Tests', () => {
    before(async () => {
        conversation = prep.conversation;
        client = prep.client;
        topic1 = await client.addTextItem(conversation.convId, {
            subject: `${Date.now()}1`,
            content: `${Date.now()}a`
        });
        topic2 = await client.addTextItem(conversation.convId, {
            subject: `${Date.now()}2`,
            content: `${Date.now()}b`
        });
        topic2Item = await client.addTextItem(conversation.convId, {
            parentId: topic2.itemId,
            content: `${Date.now()} item2 Last Item`
        });
        topic3 = await client.addTextItem(conversation.convId, {
            subject: `${Date.now()}3`,
            content: `${Date.now()}c`
        });
    });

    it('function: getConversationTopics', async () => {
        if (!client.getConversationTopics) {
            console.log('API not yet supported');
            assert(true);
            return;
        }
        const res = await client.getConversationTopics(conversation.convId, {maxNumberOfTopics: 2});
        assert(res && res.conversationTopics.length === 2 && res.hasOlderTopics);
    });

    it('function: getConversationTopics, with {timestamp:topic3.creationTime - 1}', async () => {
        if (!client.getConversationTopics) {
            console.log('API not yet supported');
            assert(true);
            return;
        }
        const res = await client.getConversationTopics(conversation.convId, {timestamp: topic3.creationTime - 1});
        const topics = res.conversationTopics;
        assert(topics && !topics.some(topic => topic.parentItem.itemId === topic3.itemId) && topics.some(topic => topic.parentItem.itemId === topic2.itemId) && topics.some(topic => topic.parentItem.itemId === topic1.itemId));
    });

    it('function: getConversationTopics, with {timestamp:topic3.creationTime + 1}', async () => {
        if (!client.getConversationTopics) {
            console.log('API not yet supported');
            assert(true);
            return;
        }
        const res = await client.getConversationTopics(conversation.convId, {timestamp: topic3.creationTime + 1});
        const topics = res.conversationTopics;
        assert(topics && topics.some(topic => topic.parentItem.itemId === topic3.itemId) && topics.some(topic => topic.parentItem.itemId === topic2.itemId) && topics.some(topic => topic.parentItem.itemId === topic1.itemId));
    });

    it('function: getConversationTopics', async () => {
        if (!client.getConversationTopics) {
            console.log('API not yet supported');
            assert(true);
            return;
        }
        const res = await client.getConversationTopics(conversation.convId);
        const topics = res.conversationTopics;
        assert(topics && topics.some(topic => topic.parentItem.itemId === topic1.itemId) && topics.some(topic => topic.parentItem.itemId === topic2.itemId && topic.lastItem.itemId === topic2Item.itemId) && topics.some(topic => topic.parentItem.itemId === topic3.itemId));
    });
});

<|start_filename|>test/node/user-presence.js<|end_filename|>
'use strict';

const assert = require('assert');
const Circuit = require('../../circuit-node');
const helper = require('./helper');
const prep = require('../preparation');
Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);

let client;
let client2;
let user2;
let presence;
describe('User Presence', () => {
    before(async () => {
        client = prep.client;
        client2 = prep.client2;
        user2 = client2.loggedOnUser;
    });

    it('function: subscribePresence', async () => {
        await client.subscribePresence([user2.userId]);
    });

    it('function: getPresence', async () => {
        const res = await client.getPresence([user2.userId]);
        presence = res[0];
        assert(presence.userId === user2.userId);
    });

    it('functions: [setStatusMessage, getStatusMessage], with event: userPresenceChanged', async () => {
        const message = `${Date.now()}a`;
        await Promise.all([
            client2.setStatusMessage(message),
            helper.expectEvents(client, [{
                type: 'userPresenceChanged',
                predicate: evt => evt.presenceState.userId === user2.userId && evt.presenceState.statusMessage === message
            }])
        ]);
        const res = await client2.getStatusMessage();
        assert(res === message);
    });

    it('functions: [setPresence, getPresence], with event: userPresenceChanged', async () => {
        const newState = Circuit.Enums.PresenceState.DND;

        await Promise.all([
            client2.setPresence({
                state: newState,
                dndUntil: Date.now() + 5000
            }),
            helper.expectEvents(client, [{
                type: 'userPresenceChanged',
                predicate: evt => evt.presenceState.userId === user2.userId && evt.presenceState.state === newState
            }])
        ]);

        const res = await client.getPresence([user2.userId]);
        presence = res[0];
        assert(presence && presence.userId === user2.userId && presence.state === newState);
    });

    it('function: unsubscribePresence', async () => {
        await client.unsubscribePresence([user2.userId]);
    });
});

<|start_filename|>test/browser/whiteboard.js<|end_filename|>
'use strict';

import { PeerUser } from '../peer-user.js';
import { expectEvents, sleep } from '../helper.js';
import config from './config.js'

const assert = chai.assert;
let client;
let peerUser1;
let call;
let whiteboard;
let condition = 'width="562.25537109375" x="356.74554443359375" y="170.75135803222656"'; // used to define element and verify in getWhiteboard
let element = `<rect  circuit:creatorId="1" circuit:orderId="1" fill="#000000" fill-opacity="0" height="311.0114288330078" stroke="#000000" stroke-width="2" ${condition}/>`;
let elementId;
let URL = 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQsAKQEnejZV5fSgujJwnAWdPHLytN3MxOSDqbzCPxwoSXufvLOlg';
function loadXHR(url) {
    return new Promise((resolve, reject) => {
        try {
            var xhr = new XMLHttpRequest();
            xhr.open('GET', url);
            xhr.responseType = 'blob';
            xhr.onerror = () => {reject('Network error.')};
            xhr.onload = () => {
                if (xhr.status === 200) {resolve(xhr.response)}
                else {reject('Loading error:' + xhr.statusText)}
            };
            xhr.send();
        } catch(err) { 
            reject(err.message);
        }
    });
}
describe('Whiteboard tests', async function() {
    this.timeout(300000);

    before(async function() {
        Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);
        client = new Circuit.Client(config.config);
        const res = await Promise.all([PeerUser.create(), client.logon(config.credentials)]);
        peerUser1 = res[0];
        const conversation = await client.createGroupConversation([peerUser1.userId], 'SDK Test: Whiteboard');
        const result = await Promise.all([
            client.startConference(conversation.convId, {audio: true, video: false}),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.call.state === Circuit.Enums.CallStateName.Initiated
            }, {
                type: 'callStatus',
                predicate: evt => evt.call.state === Circuit.Enums.CallStateName.Waiting
            }])
        ]);
        call = result[0];
        document.querySelector('#localVideo').srcObject = call.localVideoStream;
        await sleep(5000);
    });

    after(async function() {
        document.querySelector('#localVideo').srcObject = null;
        await client.endCall(call.callId);
        await Promise.all([peerUser1.destroy(), client.logout()]);
    });
    

    afterEach(async function() {
        client.removeAllListeners();
    });

    it('function: enableWhiteboard, raises event: whiteboardEnabled', async () => {
        const res = await Promise.all([
            client.enableWhiteboard(call.callId, {width: 800, height: 400}),
            expectEvents(client, [{
                type: 'whiteboardEnabled',
                predicate: evt => evt.enabled && evt.whiteboard.viewbox.width === 800 && evt.whiteboard.viewbox.height === 400
            }])
        ]);
        whiteboard = res[1] && res[1].whiteboard;
        assert(whiteboard && res[1].enabled);
    });

    it('function: getWhiteboard', async () => {
        whiteboard = await client.getWhiteboard(call.callId);
        assert(whiteboard.callId === call.callId && whiteboard.viewbox.width === 800 && whiteboard.viewbox.height === 400 && !whiteboard.background && !whiteboard.background);
    });

    it('functions: [setWhiteboardBackground, getWhiteboard]', async () => {
        const img = await loadXHR(URL);
        await client.setWhiteboardBackground(call.callId, img);
        await sleep(3000); // must wait for whiteboard background to be set because no event to listen to
        whiteboard = await client.getWhiteboard(call.callId);
        assert(whiteboard.background);
    });

    it('functions: [clearWhiteboardBackground, getWhiteboard]', async () => {
        await client.clearWhiteboardBackground(call.callId);
        await sleep(3000); // must wait for whiteboard background to be cleared because no event to listen to
        whiteboard = await client.getWhiteboard(call.callId);
        assert(!whiteboard.background);
    });

    it('function: addWhiteboardElement, raises event: whiteboardElement', async () => {
        // first element is for testing clearWhiteboard
        await client.addWhiteboardElement(call.callId, `<rect  circuit:creatorId="1" circuit:orderId="1" fill="#000000" fill-opacity="0" height="1" stroke="#000000" stroke-width="2" width="1" x="1" y="1"/>`); 
        const res = await Promise.all([
            client.addWhiteboardElement(call.callId, element),
            expectEvents(client, [{
                type: 'whiteboardElement',
                predicate: evt => evt.action === 'added' && evt.element.xmlElement.includes(condition)
            }])
        ]);
        elementId = res[1].element && res[1].element.elementId;
        assert(res[1].action === 'added' && res[1].element.xmlElement.includes(condition));
    });

    it('function: getWhiteboard, verifies element', async () => {
        whiteboard = await client.getWhiteboard(call.callId);
        assert(whiteboard.elements && whiteboard.elements.some(elm => elm.elementId.xmlId === elementId.xmlId));
    });

    it('function: removeWhiteboardElement, raises event: whiteboardElement', async () => {
        await Promise.all([
            client.removeWhiteboardElement(call.callId, elementId.xmlId),
            expectEvents(client, [{
                type: 'whiteboardElement',
                predicate: evt => evt.action === 'removed' && evt.elementId.xmlId === elementId.xmlId
            }])
        ]);
        whiteboard = await client.getWhiteboard(call.callId);
        assert(!whiteboard.elements.some(elm => elm.elementId.xmlId === elementId.xmlId));
    });

    it('function: undoWhiteboard, raises event: whiteboardElement', async () => {
        await Promise.all([
            client.undoWhiteboard(call.callId, 1),
            expectEvents(client, [{
                type: 'whiteboardElement',
                predicate: evt => evt.action === 'added' && evt.element.elementId.xmlId === elementId.xmlId
            }])
        ]);
    });

    it('function: toggleWhiteboardOverlay [ON], raises event: whiteboardOverlayToggled', async () => {
        await Promise.all([
            client.toggleWhiteboardOverlay(call.callId),
            expectEvents(client, [{
                type: 'whiteboardOverlayToggled'
            }])
        ]);
        whiteboard = await client.getWhiteboard(call.callId);
        assert(whiteboard.overlay);
    });

    it('function: toggleWhiteboardOverlay [OFF], raises event: whiteboardOverlayToggled', async () => {
        await Promise.all([
            client.toggleWhiteboardOverlay(call.callId),
            expectEvents(client, [{
                type: 'whiteboardOverlayToggled'
            }])
        ]);
        whiteboard = await client.getWhiteboard(call.callId);
        assert(!whiteboard.overlay);
    });

    it('function: clearWhiteboard, raises event: whiteboardCleared', async () => {
        await Promise.all([
            client.clearWhiteboard(call.callId),
            expectEvents(client, [{
                type: 'whiteboardCleared'
            }])
        ]);
        whiteboard = await client.getWhiteboard(call.callId);
        assert(!whiteboard.elements);
    });

    it('function: disableWhiteboard, raises event: whiteboardEnabled', async () => {
        await Promise.all([
            client.disableWhiteboard(call.callId),
            expectEvents(client, [{
                type: 'whiteboardEnabled',
                predicate: evt => !evt.enabled
            }])
        ]);
    });
});

<|start_filename|>test/node/community.js<|end_filename|>
'use strict';

const assert = require('assert');
const Circuit = require('../../circuit-node');
const prep = require('../preparation');
Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);

let client;
let client2;
let user2;
let community;
describe('Community', () => {
    before(async () => {
        client = prep.client;
        client2 = prep.client2;
        user2 = client2.loggedOnUser;
    });

    it('function: createCommunity', async () => {
        const topic = `${Date.now()}a`;
        const description = `${Date.now()}b`;
        community = await client.createCommunity(null, topic, description);
        assert(community && community.type === Circuit.Enums.ConversationType.COMMUNITY && community.topic === topic && community.description === description);
    });

    it('function: joinCommunity', async () => {
        if (!client2.joinCommunity) {
            console.log('API not yet supported');
            assert(true);
            return;
        }
        const res = await client2.joinCommunity(community.convId);
        assert(res && res.participants.some(participant => participant.userId === user2.userId));
    });
});

<|start_filename|>test/browser/directcall.js<|end_filename|>
'use strict';

import { PeerUser } from '../peer-user.js';
import { expectEvents, updateRemoteVideos } from '../helper.js';
import config from './config.js'

const assert = chai.assert;
let client;
let peerUser;
let call;
describe('Direct Call', async function() {
    this.timeout(300000);
    
    before(async function() {
        Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);
        client = new Circuit.Client(config.config);
        const res = await Promise.all([PeerUser.create(), client.logon(config.credentials)]);
        peerUser = res[0];
    });

    after(async function() {
        document.querySelector('#localVideo').srcObject  = null;
        await Promise.all([peerUser.destroy(), client.logout()]);
    });

    afterEach(async function() {
        client.removeAllListeners();
    });

    it('function: makeCall, with event: callStatus with states: [Initiated, Delivered]', async () => {
        const res = await Promise.all([
            client.makeCall(peerUser.userId, {audio: true, video: true}, true),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.call.state === Circuit.Enums.CallStateName.Initiated
            }, {
                type: 'callStatus',
                predicate: evt => evt.call.state === Circuit.Enums.CallStateName.Delivered
            }])
        ]);
        call = res[0];
        assert(call.callId);
        document.querySelector('#localVideo').srcObject = call.localVideoStream;
    });

    it('function: answerCall, with event: callStatus with reasons: [remoteStreamUpdated, callStateChanged]', async () => {
        updateRemoteVideos(client);
        const res = await Promise.all([
            peerUser.exec('answerCall', call.callId, {audio: true, video: true}),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'remoteStreamUpdated'
            }, {
                type: 'callStatus',
                predicate: evt => evt.reason === 'callStateChanged' && evt.call.state === Circuit.Enums.CallStateName.Active
            }])
        ]);
        assert(res[1].call.callId === call.callId && res[1].call.state === Circuit.Enums.CallStateName.Active);
    });

    it('function: endCall, with event: callEnded', async () => {
        updateRemoteVideos(client);
        const res = await Promise.all([client.endCall(call.callId), expectEvents(client, ['callEnded'])]);
        assert(res[1].call.callId === call.callId);
    });
});


<|start_filename|>test/browser/call-recording.js<|end_filename|>
'use strict';

import { PeerUser } from '../peer-user.js';
import { expectEvents, sleep } from '../helper.js';
import config from './config.js'

const assert = chai.assert;
let client;
let peerUser;
let call;
let recording;
describe('Call Recording', async function() {
    this.timeout(300000);

    before(async function() {
        Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);
        client = new Circuit.Client(config.config);
        const res = await Promise.all([PeerUser.create(), client.logon(config.credentials)]);
        peerUser = res[0];
        const conversation = await client.createGroupConversation([peerUser.userId], 'SDK Test: Call Recording');
        call = await client.startConference(conversation.convId, {audio: false, video: false});
        await expectEvents(client, [{
            type: 'callStatus',
            predicate: evt => evt.call.state === Circuit.Enums.CallStateName.Initiated
        }, {
            type: 'callStatus',
            predicate: evt => evt.call.state === Circuit.Enums.CallStateName.Waiting
        }]);
        await sleep(5000);
        await Promise.all([
            peerUser.exec('joinConference', call.callId, {audio: false, video: false}),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'callStateChanged' && evt.call.state === Circuit.Enums.CallStateName.Active
            }, {
                type: 'callStatus',
                predicate: evt => evt.reason === 'participantJoined'
            }])
        ]);
        call = await client.findCall(call.callId);
        document.querySelector('#localVideo').srcObject = call.localVideoStream;
    });

    after(async function() {
        document.querySelector('#localVideo').srcObject = null;
        await Promise.all([peerUser.destroy(), client.logout()]);
    });

    afterEach(async function() {
        client.removeAllListeners();
    });

    it('functions: [startRecording, stopRecording], raises event: callStatus with reason: callRecording', async () => {
        await Promise.all([
            client.startRecording(call.callId),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'callRecording' && evt.call.recording.state === Circuit.Enums.RecordingInfoState.STARTED

            }])
        ]);
        await sleep(3000); // allow 3 seconds for the recording time
        await Promise.all([
            client.stopRecording(call.callId),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'callRecording' && evt.call.recording.state === Circuit.Enums.RecordingInfoState.STOPPED
            }])
        ]);
        // user must end call before the recording appears
        await Promise.all([
            client.leaveConference(call.callId),
            peerUser.exec('leaveConference', call.callId),
        ]);
        await sleep(3000);
        const res = await client.getConversationItems(call.convId);
        recording = res.find(item => item.type === Circuit.Enums.ConversationItemType.RTC && item.rtc);
        assert(recording);
    });

    it('function: deleteRecording, raises event: itemUpdated', async () => {
        await Promise.all([
            client.deleteRecording(recording.itemId),
            expectEvents(client, [{
                type: 'itemUpdated',
                predicate: evt => evt.item.itemId === recording.itemId && evt.item.rtc.type === Circuit.Enums.RTCItemType.ENDED
            }]) 
        ]);
    });
});


<|start_filename|>test/node/conversation-group.js<|end_filename|>
'use strict';

const assert = require('assert');
const Circuit = require('../../circuit-node');
const helper = require('./helper');
const prep = require('../preparation');
Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);

let client;
let user;
let client2;
let user2;
let conversation;
describe('Conversation Group', () => {
    before(async () => {
        client = prep.client;
        user = client.loggedOnUser;
        client2 = prep.client2;
        user2 = client2.loggedOnUser;
    });

    it('function: createGroupConversation, with event: conversationCreated', async () => {
        const topic = `${Date.now()}a`;
        const res = await Promise.all([
            client.createGroupConversation([user2.userId], topic),
            helper.expectEvents(client, [{
                type: 'conversationCreated',
                predicate: evt => evt.conversation.topic === topic && evt.conversation.participants.includes(user.userId) && evt.conversation.participants.includes(user2.userId)
            }])
        ]);
        conversation = res[0];
        assert(conversation && conversation.participants.includes(user.userId) && conversation.participants.includes(user2.userId));
    });

    it('function: getConversationById', async () => {
        const res = await client.getConversationById(conversation.convId);
        assert(res && res.convId === conversation.convId && res.participants.includes(user.userId) && res.participants.includes(user2.userId));
    });

    it('function: removeParticipant, with event: conversationUpdated', async () => {
        const res = await Promise.all([
            client.removeParticipant(conversation.convId, user2.userId),
            helper.expectEvents(client, [{
                type: 'conversationUpdated',
                predicate: evt => evt.conversation.convId === conversation.convId && !evt.conversation.participants.includes(user2.userId)
            }])
        ]);
        if (res[1].conversation.convId === conversation.convId && !res[1].conversation.participants.includes(user2.userId)) {  
            conversation = res[1].conversation;
            assert(true);
        } else {
            assert(false);
        }
    });

    it('function: addParticipant, with event: conversationUpdated', async () => {
        const res = await Promise.all([
            client.addParticipant(conversation.convId, user2.userId),
            helper.expectEvents(client, [{
                type: 'conversationUpdated',
                predicate: evt => evt.conversation.convId === conversation.convId && evt.conversation.participants.includes(user2.userId)
            }])
        ]);
        if (res[0].convId === conversation.convId && res[0].participants.includes(user2.userId)) {  
            conversation = res[0];
            assert(true);
        } else {
            assert(false);
        }
    });

    it('function: getConversationParticipants', async () => {
        const res = await client.getConversationParticipants(conversation.convId);
        assert(res && res.participants.some(u => u.userId === user.userId) && res.participants.some(u => u.userId === user2.userId));
    });

    it('function: updateConversation, with event: conversationUpdated', async () => {
        const topic = `${Date.now()}z`;
        const data = {
            topic: topic
        }
        const res = await Promise.all([
            client.updateConversation(conversation.convId, data),
            helper.expectEvents(client, [{
                type: 'conversationUpdated',
                predicate: evt => evt.conversation.convId === conversation.convId && evt.conversation.topic === topic
            }])
        ]);
        const convId = conversation.convId;
        conversation = res[0];
        assert(conversation.convId === convId && conversation.topic === topic);
    });

    // Requires MODERATION permission
    it('functions: [moderateConversation, getConversationById], with event: conversationUpdated', async () => {
        await Promise.all([
            client.moderateConversation(conversation.convId),
            helper.expectEvents(client, [{
                type: 'conversationUpdated',
                predicate: evt => evt.conversation.convId === conversation.convId && evt.conversation.moderators.includes(user.userId)
            }])            
        ]);
        const res = await client.getConversationById(conversation.convId);
        assert(res.convId === conversation.convId && res.moderators.includes(user.userId));
    });

    it('functions: [unmoderateConversation, getConversationById], with event: conversationUpdated', async () => {
        await Promise.all([
            client.unmoderateConversation(conversation.convId),
            helper.expectEvents(client, [{
                type: 'conversationUpdated',
                predicate: evt => evt.conversation.convId === conversation.convId && (!evt.conversation.moderators || !evt.conversation.moderators.includes(user.userId))
            }])            
        ]);
        const res = await client.getConversationById(conversation.convId);
        assert(res.convId === conversation.convId && (!res.moderators || !res.moderators.includes(user.userId)));
    });
});

<|start_filename|>test/preparation.js<|end_filename|>
// client logs on to create a global conversation for other tests to use
if (!exports.conversation) {
    before(async () => {
        try {
            const Circuit = require('../circuit-node');
            const config = require('./node/config.json');
            const client = new Circuit.Client(config.bot1);
            module.exports.client = client;
            const client2 = new Circuit.Client(config.bot2);
            module.exports.client2 = client2;
            const client3 = new Circuit.Client(config.sdktester1.config);
            await client.logon();
            const user2 = await client2.logon();
            const user3 = await client3.logon(config.sdktester1.credentials);
            module.exports.conversation = await client.createGroupConversation([user2.userId], `globalTestConversation${Date.now()}`);
            const existingConversations = await client.getConversations({ numberOfConversations: 10 });
            // Ensure there are at least 10 conversations present
            for (let i = 0; i < 10 - existingConversations.length; i++) {
                await client.createConferenceBridge(`newConversation${i + 1}`);
            }
            module.exports.phoneNumber = {
                phoneNumber: `+1561${Math.random().toString().substring(2,9)}`,
                type: `WORK`,
                isExternallyManaged: false
            }
            await client3.updateUser({
                userId: user3.userId,
                phoneNumbers: [module.exports.phoneNumber]
            });
            await client.logout();
            await client2.logout();
            await client3.logout();
        } catch (e) {
            console.error('Error running preparation:', e);
            process.exit(1);
        }
    });
}

<|start_filename|>test/node/helper.js<|end_filename|>
'use strict';

(function (root, factory) {
	if (typeof module === 'object' && module.exports) {
			module.exports = factory();
	} else {
		root.helper = factory();
	}
}(typeof self !== 'undefined' ? self : this, function () {

	function remove(array, element) {
		const index = array.indexOf(element);
		array.splice(index, 1);
	}

	/**
	 * Delay processing. Returns a promise.
	 * @param {Number} ms Time to sleep in ms
	 */
	function sleep(ms) {
		return new Promise(resolve => setTimeout(resolve, ms));
	}

	/**
	 * expectEvents
	 * @param {Object} client Circuit client instance
	 * @param {Object[]} events Array with event type and optional predicate function
	 * @returns {Promise}
	 */
	function expectEvents(client, events) {
		return new Promise((resolve, reject) => {
			const evts = events.map(e =>
				typeof e === 'string' ? { type: e, predicate: () => { return true; } } : e
			)
			const fn = evt => {
				const matchingEvents = evts.filter(e => e.type === evt.type);
				if (matchingEvents.length) {
					const resolvedEvents = matchingEvents.filter(me => !me.predicate || me.predicate(evt));
					resolvedEvents.forEach(re => remove(evts, re));
					if (!evts.length) {
						events.forEach(event => client.removeEventListener(event.type, fn))
						resolve(evt);
					}
				}
			}
			evts.forEach(event => client.addEventListener(event.type, fn));
		});
	}

	function logEvents(client, events) {
		events.forEach(e =>
			client.addEventListener(e, evt => {
				let msg = `Received ${e} event.`;
				evt.reason && (msg += ` Reason: ${evt.reason}.`);
				evt.call && (msg += ` State: ${evt.call.state}.`);
				console.log(msg);
			})
		);
	}

	async function assertThrowsAsync(fn, regExp) {
		let f = () => {};
		try {
			await fn();
		} catch(e) {
			f = () => {throw e};
		} finally {
			assert.throws(f, regExp);
		}
	}

	return {
		sleep: sleep,
		logEvents: logEvents,
		expectEvents: expectEvents,
		assertThrowsAsync: assertThrowsAsync
	};
}));


<|start_filename|>File.js<|end_filename|>
///////////////////////////////////////////////////////////////////////////////////////////////
// File for Circuit Node.js
// Based on npm File module
///////////////////////////////////////////////////////////////////////////////////////////////

(function () {
    'use strict';

    var fs = require('fs'),
        path = require('path'),
        mime = require('mime');

    // string
    // stream
    // buffer
    function File(input) {
        var self = this;

        function updateStat(stat) {
            self.stat = stat;
            self.lastModifiedDate = self.stat.mtime;
            self.size = self.stat.size;
        }

        if (typeof input === 'string') {
            self.path = input;
        } else {
            Object.keys(input).forEach(function (k) {
                self[k] = input[k];
            });
        }

        self.name = self.name || path.basename(self.path || '');
        if (!self.name) {
            throw new Error('No name');
        }
        if (mime.getType) {
            self.type = self.type || mime.getType(self.name);
        } else if (mime.lookup) {
            self.type = self.type || mime.lookup(self.name);
        }

        if (!self.path) {
            if (self.buffer) {
                self.size = self.buffer.length;
            } else if (!self.stream) {
                throw new Error('No input, nor stream, nor buffer.');
            }
            return;
        }

        if (!self.jsdom) {
            return;
        }

        if (!self.async) {
            updateStat(fs.statSync(self.path));
        } else {
            fs.stat(self.path, function (err, stat) {
                updateStat(stat);
            });
        }
    }

    module.exports = File;
}());


<|start_filename|>test/browser/video-sharing.js<|end_filename|>
'use strict';

import { PeerUser } from '../peer-user.js';
import { expectEvents, sleep } from '../helper.js';
import config from './config.js'

const assert = chai.assert;
let client;
let peerUser1;
let call;
describe('Video Sharing', async function() {
    this.timeout(300000);

    // before(async function() {
    //     Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);
    //     client = new Circuit.Client(config.config);
    //     const res = await Promise.all([PeerUser.create(), client.logon(config.credentials)]);
    //     peerUser1 = res[0];
    //     const conversation = await client.createGroupConversation([peerUser1.userId], 'SDK Test: Video Sharing');
    //     call = await client.startConference(conversation.convId, {audio: true, video: false});
    //     await expectEvents(client, [{
    //         type: 'callStatus',
    //         predicate: evt => evt.call.state === Circuit.Enums.CallStateName.Initiated
    //     }, {
    //         type: 'callStatus',
    //         predicate: evt => evt.call.state === Circuit.Enums.CallStateName.Waiting
    //     }]);
    //     await sleep(5000); // wait to make sure the call is ready to be joined
    //     await Promise.all([
    //         peerUser1.exec('joinConference', call.callId, {audio: true, video: false}),
    //         expectEvents(client, [{
    //             type: 'callStatus',
    //             predicate: evt => evt.reason === 'participantJoined'
    //         }])
    //     ]);
    //     call = await client.findCall(call.callId);
    //     document.querySelector('#localVideo').srcObject = call.localVideoStream;
    // });

    // after(async function() {
    //     document.querySelector('#localVideo').srcObject = null;
    //     await Promise.all([peerUser1.destroy(), client.logout()]);
    // });

    // afterEach(async function() {
    //     client.removeAllListeners();
    // });

    // TODO: REMOVE WHEN FIXED
    it('Video Sharing disabled until later', () => {});


    // it('function: toggleVideo [ON], raises event: callStatus with reason: callStateChanged', async () => {
    //     await Promise.all([
    //         client.toggleVideo(call.callId),
    //         expectEvents(client, [{
    //             type: 'callStatus',
    //             predicate: evt => evt.reason === 'callStateChanged' && evt.call.localStreams.video
    //         }])
    //     ]);
    // });

    // it('function: findCall, verifies audio and video stream objects of users', async () => {
    //     await sleep(5000); // wait for all user's audio video streams to update
    //     call = await client.findCall(call.callId);
    //     assert(call.remoteAudioStream.active && call.localStreams.video && call.participants.every(participant => call.remoteVideoStreams.some(stream => stream.streamId === participant.streamId)));
    // });

    // it('function: changeHDVideo [ON], raises event: callStatus with reason: sdpConnected', async () => {
    //     await Promise.all([
    //         client.changeHDVideo(call.callId, true),
    //         expectEvents(client, [{
    //             type: 'callStatus',
    //             predicate: evt => evt.reason === 'sdpConnected' && evt.call.localMediaType.hdVideo
    //         }])
    //     ]);
    // });

    // it('function: changeHDVideo [OFF], raises event: callStatus with reason: sdpConnected', async () => {
    //     await Promise.all([
    //         client.changeHDVideo(call.callId, false),
    //         expectEvents(client, [{
    //             type: 'callStatus',
    //             predicate: evt => evt.reason === 'sdpConnected' && !evt.call.localMediaType.hdVideo
    //         }])
    //     ]);
    // });

    // it('function: toggleVideo [OFF], raises event: callStatus with reason: callStateChanged', async () => {
    //     await Promise.all([
    //         client.toggleVideo(call.callId),
    //         expectEvents(client, [{
    //             type: 'callStatus',
    //             predicate: evt => evt.reason === 'callStateChanged' && !evt.call.localStreams.video
    //         }])
    //     ]);
    // });
});

<|start_filename|>test/runner.js<|end_filename|>
'use strict';

const path = require('path');
const util = require('util');
const puppeteer = require('puppeteer');
const globby = require('globby');
const config = require('./peer-config.json');

let browserTimeout;
let puppeteerOptions;
let peerUrl;

// Pool of peer users
const peerUserPool = config.credentials.map(user => user.username);
const peerUserCredentials = new Map(config.credentials.map((i) => [i.username, i]));

// Hashtable of peer browser pages indexed by username
const peerBrowsers = {};

function initMocha(reporter) {
    console.log = (console => {
        const log = console.log.bind(console);
        return (...args) => args.length ? log(...args) : log('');
    })(console);

    function shimMochaInstance(m) {
        m.setup({ reporter: Mocha.reporters[reporter] || Mocha.reporters.spec });

        const run = m.run.bind(m);
        m.run = () => {
            const all = [], pending = [], failures = [], passes = [];

            function error(err) {
                if (!err) return {};

                let res = {};
                Object.getOwnPropertyNames(err).forEach(key => res[key] = err[key]);
                return res;
            }

            function clean(test) {
                return {
                    title: test.title,
                    fullTitle: test.fullTitle(),
                    duration: test.duration,
                    err: error(test.err)
                };
            }

            function result(stats) {
                return {
                    result: {
                        stats: {
                            tests: all.length,
                            passes: passes.length,
                            pending: pending.length,
                            failures: failures.length,
                            start: stats.start.toISOString(),
                            end: stats.end.toISOString(),
                            duration: stats.duration
                        },
                        tests: all.map(clean),
                        pending: pending.map(clean),
                        failures: failures.map(clean),
                        passes: passes.map(clean)
                    },
                    coverage: window.__coverage__
                };
            }

            function setResult() {
                !window.__mochaResult__ && (window.__mochaResult__ = result(this.stats));
            }

            const runner = run(() => setTimeout(() => setResult.call(runner), 0))
                .on('pass', test => { passes.push(test); all.push(test); })
                .on('fail', test => { failures.push(test); all.push(test); })
                .on('pending', test => { pending.push(test); all.push(test); })
                .on('end', setResult);

            return runner;
        };
    }

    function shimMochaProcess(M) {
        // Mocha needs a process.stdout.write in order to change the cursor position.
        !M.process && (M.process = {});
        !M.process.stdout && (M.process.stdout = {});

        M.process.stdout.write = data => console.log('stdout:', data);
        M.reporters.Base.useColors = true;
        M.reporters.none = function None(runner) {
            M.reporters.Base.call(this, runner);
        };
    }

    Object.defineProperty(window, 'mocha', {
        get: function() { return undefined },
        set: function(m) {
            shimMochaInstance(m);
            delete window.mocha;
            window.mocha = m
        },
        configurable: true
    })

    Object.defineProperty(window, 'Mocha', {
        get: function() { return undefined },
        set: function(m) {
            shimMochaProcess(m);
            delete window.Mocha;
            window.Mocha = m;
        },
        configurable: true
    });
}

async function handlePeerUserCommand({name, args, username}) {
    if (name === 'create') {
        // spin up new browser for the new Peer User
        const browser = await puppeteer.launch(puppeteerOptions)
        const page = await browser.newPage();
        page.on('console', handleConsole);
        page.on('pageerror', console.error);
        await page.goto(peerUrl);

        // Close browser when user logs out, or on timeout
        page.waitForFunction(() => window.__peeruserlogout__, { timeout: browserTimeout })
            .then(() => browser.close())
            .catch(console.error);

        // Add new page to hastable
        const username = peerUserPool.shift();
        peerBrowsers[username] = page;

        // Create client instance and login
        return await page.evaluate(async (config, credentials) => {
            window.__client__ = new Circuit.Client(config);
            return await window.__client__.logon(credentials);
        }, config.config, peerUserCredentials.get(username));
    }

    const page = peerBrowsers[username];

    if (name === 'destroy') {
        // Log user out. Triggers closing of browser.
        await page.evaluate(async () => {
            await window.__client__.logout();
            window.__peeruserlogout__ = true;
        });

        // Return user to the pool of peers
        peerUserPool.unshift(username);
        return;
    }

    // Execute an async API call on the client instance and return the result
    return await page.evaluate(async (name, args) => {
        return await window.__client__[name](...args);
    }, name, args);
}

function handleConsole(msg) {
    const args = msg._args;
    Promise.all(args.map(a => a.jsonValue()))
        .then(args => {
            // process stdout stub
            let isStdout = args[0] === 'stdout:';
            isStdout && (args = args.slice(1));
            //
            let msg = util.format(...args);

            !isStdout && (msg += '\n');
            process.stdout.write(msg);
        })
        .catch(err => process.stdout.write(err));
}

module.exports = async options => {
    let { files, port, reporter, timeout, width, height, x, y, args, executablePath, visible } = options;
    browserTimeout = timeout || 60000;
    port = port || 3000;
    peerUrl = `http://localhost:${port}/test/peer-template.html`;


    x = x || 0;
    y = y || 0;
    args.push(`window-position=${x},${y}`);

    width = width || 400;
    height = height || 400;
    args.push(`window-size=${width},${height}`);

    args = [].concat(args || []).map(arg => '--' + arg);

    puppeteerOptions = {
        ignoreHTTPSErrors: true,
        headless: !visible,
        executablePath,
        args
    };

    const paths = await globby(files);

    const result = [];

    for (let file of paths) {
        const res = await puppeteer
            .launch(puppeteerOptions)
            .then(browser => browser.newPage()
                .then(page => {
                    page.on('console', handleConsole);
                    page.on('pageerror', err => console.error(err));

                    return page.exposeFunction('onPeerUserCmd', handlePeerUserCommand)
                        .then(() => page.evaluateOnNewDocument(initMocha, reporter))
                        .then(() => page.goto(`http://localhost:${port}/${file}`))
                        .then(() => page.waitForFunction(() => window.__mochaResult__, { timeout: timeout }))
                        .then(() => page.evaluate(() => window.__mochaResult__))
                        .then(res => {
                            browser.close();
                            return res.result;
                        });
                }));
        result.push(res);
    }

    return result;
};


<|start_filename|>WebSocket.js<|end_filename|>
///////////////////////////////////////////////////////////////////////////////////////////////
// WebSocket for Circuit Node.js
///////////////////////////////////////////////////////////////////////////////////////////////

var NodeWebSocket = require('ws');
var NodeSDK = require('./NodeSDK');

module.exports = function (target, cookie) {
    'use strict';
    var logger = NodeSDK.logger;
    var wsOptions = {
        headers: {
            Cookie: cookie
        },
        rejectUnauthorized: NodeSDK.rejectUnauthorized,
        agent: NodeSDK.proxyAgent
    };
    logger.debug('[WebSocket]: Initiated websocket with ' + JSON.stringify(wsOptions));
    return new NodeWebSocket(target, wsOptions);
};


<|start_filename|>test/browser/call-joining.js<|end_filename|>
'use strict';

import { PeerUser } from '../peer-user.js';
import { expectEvents, sleep } from '../helper.js';
import config from './config.js'

const assert = chai.assert;
let client;
let peerUser1, peerUser2;
let call;
describe('Call Joining', async function() {
    this.timeout(300000);

    before(async function() {
        Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);
        client = new Circuit.Client(config.config);
        const res = await Promise.all([PeerUser.create(), PeerUser.create(), client.logon(config.credentials)]);
        peerUser1 = res[0];
        peerUser2 = res[1];
        const conversation = await client.createGroupConversation([peerUser2.userId], 'SDK Test: Call Joining');
        call = await client.startConference(conversation.convId, {audio: true, video: false});
        await expectEvents(client, [{
            type: 'callStatus',
            predicate: evt => evt.call.state === Circuit.Enums.CallStateName.Initiated
        }, {
            type: 'callStatus',
            predicate: evt => evt.call.state === Circuit.Enums.CallStateName.Waiting
        }]);
        await sleep(3000); // wait to make sure the call is ready
    });

    after(async function() {
        await Promise.all([peerUser1.destroy(), peerUser2.destroy(), client.logout()]);
    });

    afterEach(async function() {
        client.removeAllListeners();
    });

    it('function: getStartedCalls', async () => {
        const res = await peerUser2.exec('getStartedCalls');
        assert(res.some(c => c.callId === call.callId));
    })

    it('function: addParticipantToCall, raises event: callStatus with reason: participantAdded', async () => {
        await Promise.all([
            client.addParticipantToCall(call.callId, { userId: peerUser1.userId }),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'participantAdded' && evt.call.callId === call.callId && evt.participant.userId === peerUser1.userId
            }])
        ]);
        await sleep(3000); // wait to allow time so the participant can answer the call
        await Promise.all([
            peerUser1.exec('answerCall', call.callId, {audio: false, video: false}),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'participantJoined' && evt.call.callId === call.callId && evt.participant.userId === peerUser1.userId && evt.participant.isMeetingGuest
            }])
        ]);
    });

    it('function: joinConference, raises event: callStatus with reason: participantJoined', async () => {
        await Promise.all([
            peerUser2.exec('joinConference', call.callId, {audio: true, video: false}),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'participantJoined' && evt.call.callId === call.callId && evt.participant.userId === peerUser2.userId
            }])
        ]);
    });

    it('function: getCalls', async () => {
        const res = await peerUser1.exec('getCalls');
        assert(res && res.some(c => c.callId === call.callId));
    });

    it('function: dropParticipant, raises event: callStatus with reason: participantRemoved', async () => {
        await Promise.all([
            client.dropParticipant(call.callId, peerUser1.userId),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'participantRemoved' && evt.call.callId === call.callId && !evt.call.participants.some(user => user.userId === peerUser1.userId)
            }])
        ]);
    });

    it('function: leaveConference, raises event: callStatus with reason: participantRemoved', async () => {
        await Promise.all([
            peerUser2.exec('leaveConference', call.callId),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'participantRemoved' && evt.call.callId === call.callId && !evt.call.participants.some(user => user.userId === peerUser2.userId)
            }])
        ]);
    });
    
    it('function: addParticipant, raises event: callStatus with reason: participantJoined', async () => {
        await client.addParticipant(call.convId, [peerUser1.userId], true);
        await sleep(3000); // wait to allow time so the participant can answer the call
        await Promise.all([
            peerUser1.exec('answerCall', call.callId, {audio: false, video: false}),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'participantJoined' && evt.call.callId === call.callId && evt.participant.userId === peerUser1.userId && !evt.participant.isMeetingGuest
            }])
        ]);
    });

    it('function: leaveConference, raises event: callStatus with reason: participantRemoved', async () => {
        await Promise.all([
            peerUser1.exec('leaveConference', call.callId),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'participantRemoved' && evt.call.callId === call.callId && !evt.call.participants.some(user => user.userId === peerUser1.userId)
            }])
        ]);
    });

    it('function: endCall, raises event: callStatus with reason: callStateChanged', async () => {
        await Promise.all([
            client.endCall(call.callId),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'callStateChanged' && evt.call.callId === call.callId && evt.call.state === 'Terminated'
            }])
        ]);
    });
});


<|start_filename|>test/node/users.js<|end_filename|>
'use strict';

const assert = require('assert');
const Circuit = require('../../circuit-node');
const helper = require('./helper');
const prep = require('../preparation');
Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);

let client;
let user;
let client2;
let user2;
describe('Users', () => {
    before(async () => {
        client = prep.client;
        user = client.loggedOnUser;
        client2 = prep.client2;
        user2 = client2.loggedOnUser;
    });

    it('function: getLoggedOnUser', async () => {
        const res = await client.getLoggedOnUser();
        assert(res && res.userId === user.userId);
    });

    it('function: getUserById [user2]', async () => {
        const res = await client.getUserById(user2.userId);
        assert(res && res.userId === user2.userId);
    });

    it('function: getUserById [user]', async () => {
        const res = await client.getUserById(user.userId);
        assert(res && res.userId === user.userId);
    });

    it('function: getUserByEmail', async () => {
        const res = await client.getUserByEmail(user2.emailAddress);
        assert(res && res.userId === user2.userId);
    });

    it('function: getUsersById', async () => {
        const res = await client.getUsersById([user.userId, user2.userId]);
        assert(res && res.every(u => u.userId === user.userId || u.userId === user2.userId));
    });

    it('function: getUsersByEmail', async () => {
        const res = await client.getUsersByEmail([user.emailAddress, user2.emailAddress]);
        assert(res && res.every(u => u.userId === user.userId || u.userId === user2.userId));
    });

    it('functions: [updateUser, getUserById], with event: userUpdated', async () => {
        const updatedUser = {
            userId: user.userId,
            firstName: `${Date.now()}a`,
            lastName: `${Date.now()}b`,
            phoneNumbers: [{
                phoneNumber: '+15615551234',
                type: 'WORK',
                isExternallyManaged: false
            }],
            emailAddresses: [{
                type: 'WORK',
                address: '<EMAIL>'
            },{
                type: 'OTHER',
                address: '<EMAIL>'
            }],
            jobTitle: `${Date.now()}x`,
            company: `${Date.now()}z`
        }
        const oldUser = {
            userId: user.userId,
            firstName: user.firstName,
            lastName: user.lastName,
            phoneNumbers: user.phoneNumbers,
            emailAddresses: user.emailAddresses,
            jobTitle: user.jobTitle || ' ',
            company: user.company || ' '
        }
        await Promise.all([
            client.updateUser(updatedUser),
            helper.expectEvents(client, [{
                type: 'userUpdated',
                predicate: evt => evt.user.userId === user.userId && evt.user.firstName === updatedUser.firstName && evt.user.lastName === updatedUser.lastName && evt.user.jobTitle === updatedUser.jobTitle && evt.user.company === updatedUser.company && updatedUser.emailAddresses.every(emailAddress => evt.user.emailAddresses.some(e => e.address === emailAddress.address)) && updatedUser.phoneNumbers.every(number => evt.user.phoneNumbers.some(p => p.phoneNumber === number.phoneNumber))
            }])
        ]);
        const res1 = await client.getUserById(user.userId);
        const result1 = res1 && res1.userId === user.userId && res1.firstName === updatedUser.firstName && res1.lastName === updatedUser.lastName && res1.jobTitle === updatedUser.jobTitle && res1.company === updatedUser.company && updatedUser.emailAddresses.every(emailAddress => res1.emailAddresses.some(e => e.address === emailAddress.address)) && updatedUser.phoneNumbers.every(number => res1.phoneNumbers.some(p => p.phoneNumber === number.phoneNumber));
        await Promise.all([
            client.updateUser(oldUser),
            helper.expectEvents(client, [{
                type: 'userUpdated',
                predicate: evt => evt.user.userId === user.userId && evt.user.firstName === oldUser.firstName && evt.user.lastName === oldUser.lastName && evt.user.jobTitle === oldUser.jobTitle && evt.user.company === oldUser.company && oldUser.emailAddresses.every(emailAddress => evt.user.emailAddresses.some(e => e.address === emailAddress.address)) && oldUser.phoneNumbers.every(number => evt.user.phoneNumbers.some(p => p.phoneNumber === number.phoneNumber))
            }])
        ]);
        const res2 = await client.getUserById(user.userId);
        const result2 = res2 && res2.userId === user.userId && res2.firstName === oldUser.firstName && res2.lastName === oldUser.lastName && res2.jobTitle === oldUser.jobTitle && res2.company === oldUser.company && updatedUser.emailAddresses.every(emailAddress => res2.emailAddresses.some(e => e.address === emailAddress.address)) && oldUser.phoneNumbers.every(number => res2.phoneNumbers.some(p => p.phoneNumber === number.phoneNumber));
        assert(result1 && result2);
    });
});

<|start_filename|>test/node/search.js<|end_filename|>
'use strict';

const assert = require('assert');
const Circuit = require('../../circuit-node');
const config = require('./config.json');
const helper = require('./helper');
const prep = require('../preparation');
Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);

let client;
let client2;
let user2;
let conversation;
let phoneNumber;
const MEETING_POINT = {
    query: 'unit-test-dont',
    location: 'Tiverton'
};
describe('Search Tests', () => {
    before(async () => {
        client = prep.client;
        client2 = new Circuit.Client(config.sdktester1.config);
        user2 = await client2.logon(config.sdktester1.credentials);
        conversation = prep.conversation;
        phoneNumber = prep.phoneNumber;
    });

    after(async () => {
        await client2.logout();
    });

    afterEach(async () => await helper.sleep(2000));// wait to allow time for searches to return

    /*
    it('function: startBasicSearch [conversation], with event: searchStatus', async () => {
        await Promise.all([
            client.startBasicSearch(conversation.topic),
            helper.expectEvents(client, [{
                type: 'searchStatus',
                predicate: evt => evt.data.status === 'FINISHED'
            }, {
                type: 'basicSearchResults',
                predicate: evt => 
                    evt.data.searchResults.some(conv => conv.convId === conversation.convId)
            }])
        ]);
    });
*/

    it('function: startUserSearch [firstName], with event: searchStatus', async () => {
        await Promise.all([
            client.startUserSearch(user2.firstName),
            helper.expectEvents(client, [{
                type: 'searchStatus',
                predicate: evt => evt.data.status === 'FINISHED'
            }, {
                type: 'basicSearchResults',
                predicate: evt => evt.data.users.includes(user2.userId)
            }])
        ]);
    });

    it('function: startUserSearch [phoneNumber], with event: searchStatus', async () => {
        await Promise.all([
            client.startUserSearch(phoneNumber.phoneNumber),
            helper.expectEvents(client, [{
                type: 'searchStatus',
                predicate: evt => evt.data.status === 'FINISHED'
            }, {
                type: 'basicSearchResults',
                predicate: evt => evt.data.users.includes(user2.userId)
            }])
        ]);
    });

    it('function: startAdvancedUserSearch [name], with event: searchStatus and searchExactAssignedPhoneNumber = undefined', async () => {
        if (!client.startAdvancedUserSearch) {
            console.log('API not yet supported');
            assert(true);
            return;
        }
        const searchObj = {
            searchContext: Circuit.Enums.SearchContext.USER,
            query: user2.firstName,
            searchExactAssignedPhoneNumber: false
        };
        await Promise.all([
            client.startAdvancedUserSearch(searchObj),
            helper.expectEvents(client, [{
                type: 'searchStatus',
                predicate: evt => evt.data.status === 'FINISHED'
            }, {
                type: 'basicSearchResults',
                predicate: evt => evt.data.users.includes(user2.userId)
            }])
        ]);
    });

    it('function: startAdvancedUserSearch [phoneNumber], with event: searchStatus and searchExactAssignedPhoneNumber = false', async () => {
        if (!client.startAdvancedUserSearch) {
            console.log('API not yet supported');
            assert(true);
            return;
        }
        const searchObj = {
            searchContext: Circuit.Enums.SearchContext.USER,
            query: phoneNumber.phoneNumber
        };
        await Promise.all([
            client.startAdvancedUserSearch(searchObj),
            helper.expectEvents(client, [{
                type: 'searchStatus',
                predicate: evt => evt.data.status === 'FINISHED'
            }, {
                type: 'basicSearchResults',
                predicate: evt => evt.data.users.includes(user2.userId)
            }])
        ]);
    });

    it('function: startAdvancedUserSearch [phoneNumber], with event: searchStatus and searchExactAssignedPhoneNumber = true and correct number', async () => {
        if (!client.startAdvancedUserSearch) {
            console.log('API not yet supported');
            assert(true);
            return;
        }
        const searchObj = {
            searchContext: Circuit.Enums.SearchContext.USER,
            query: user2.phoneNumber,
            searchExactAssignedPhoneNumber: true
        };
        await Promise.all([
            client.startAdvancedUserSearch(searchObj),
            helper.expectEvents(client, [{
                type: 'searchStatus',
                predicate: evt => evt.data.status === 'FINISHED'
            }, {
                type: 'basicSearchResults',
                predicate: evt => evt.data.users.includes(user2.userId)
            }])
        ]);
    });

    it('function: startAdvancedUserSearch [phoneNumber], with event: searchStatus and searchExactAssignedPhoneNumber = true and incorrect number', async () => {
        if (!client.startAdvancedUserSearch) {
            console.log('API not yet supported');
            assert(true);
            return;
        }
        const searchObj = {
            searchContext: Circuit.Enums.SearchContext.USER,
            query: user2.phoneNumber && user2.phoneNumber.substring(0, user2.phoneNumber.length - 1),
            searchExactAssignedPhoneNumber: true
        };
        await Promise.all([
            client.startAdvancedUserSearch(searchObj),
            helper.expectEvents(client, [{
                type: 'searchStatus',
                predicate: evt => evt.data.status === 'NO_RESULT'
            }])
        ]);
    });

    it('function: startAdvancedUserSearch [MEETING_POINT], with event: searchStatus', async () => {
        if (!client.startAdvancedUserSearch) {
            console.log('API not yet supported');
            assert(true);
            return;
        }
        const searchObj = {
            searchContext: Circuit.Enums.SearchContext.MEETING_POINT,
            query: MEETING_POINT.query
        };
        await Promise.all([
            client.startAdvancedUserSearch(searchObj),
            helper.expectEvents(client, [{
                type: 'searchStatus',
                predicate: evt => evt.data.status === 'FINISHED'
            }, {
                type: 'basicSearchResults',
                predicate: async evt => {
                    const users = await client.getUsersById(evt.data.users);
                    return users.some(user => user.userType === Circuit.Enums.SearchContext.MEETING_POINT && user.location === MEETING_POINT.location);
                }
            }])
        ]);
    });
});

<|start_filename|>test/node/conversation-typing.js<|end_filename|>
'use strict';

const assert = require('assert');
const Circuit = require('../../circuit-node');
const helper = require('./helper');
const prep = require('../preparation');
Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);

let client;
let client2;
let user2;
let conversation;
let item;
describe('Conversation Subscribe to Typing Indicator', () => {
    before(async () => {
        conversation = prep.conversation;
        client = prep.client;
        client2 = prep.client2;
        user2 = client2.loggedOnUser;
        const content = {
            subject: `${Date.now()}a`,
            content: `${Date.now()}b`
        }
        item = await client.addTextItem(conversation.convId, content);
    });

    it('function: subscribeTypingIndicator', async () => {
        if (!client.subscribeTypingIndicator) {
            console.log('API not yet supported');
            assert(true);
            return;
        }
        await client.subscribeTypingIndicator(conversation.convId);
    });

    it('function: typing, with event: typing', async () => {
        if (!client.typing) {
            console.log('API not yet supported');
            assert(true);
            return;
        }
        await Promise.all([
            client2.typing(conversation.convId, true, item.itemId),
            helper.expectEvents(client, [{
                type: 'typing',
                predicate: evt => evt.data.participantId === user2.userId && evt.data.convId === conversation.convId && evt.data.parentItemId === item.itemId
            }])    
        ]);
    });

    it('function: unsubscribeTypingIndicator', async () => {
        if (!client.unsubscribeTypingIndicator) {
            console.log('API not yet supported');
            assert(true);
            return;
        }
        await client.unsubscribeTypingIndicator(conversation.convId);
    });
});

<|start_filename|>test/node/conversation-moderator.js<|end_filename|>
'use strict';

const assert = require('assert');
const Circuit = require('../../circuit-node');
const prep = require('../preparation');
Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);

let client;
let user;
let client2;
let user2;
let conversation;
describe('Conversation Moderator', () => {
    before(async () => {
        conversation = prep.conversation;
        client = prep.client;
        user = client.loggedOnUser;
        client2 = prep.client2;
        user2 = client2.loggedOnUser;
    });

    it('functions: [moderateConversation, getConversationById]', async () => {
        await client.moderateConversation(conversation.convId);
        conversation = await client.getConversationById(conversation.convId);
        assert(conversation.isModerated && conversation.moderators.includes(user.userId));
    });

    it('functions: [grantModeratorRights, getConversationById]', async () => {
        await client.grantModeratorRights(conversation.convId, user2.userId);
        conversation = await client.getConversationById(conversation.convId);
        assert(conversation.moderators.includes(user2.userId));
    });

    it('functions: [dropModeratorRights, getConversationById]', async () => {
        await client.dropModeratorRights(conversation.convId, user2.userId);
        conversation = await client.getConversationById(conversation.convId);
        assert(!conversation.moderators || !conversation.moderators.includes(user2.userId));
    });

    it('functions: [unmoderateConversation, getConversationById]', async () => {
        await client.unmoderateConversation(conversation.convId);
        conversation = await client.getConversationById(conversation.convId);
        assert(!conversation.isModerated);
    });
});

<|start_filename|>test/node/conversation-guest-access.js<|end_filename|>
'use strict';

const assert = require('assert');
const Circuit = require('../../circuit-node');
const prep = require('../preparation');
Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);

let client;
let user;
let conversation;
describe('Conversation Guest Access Tests', () => {
    before(async () => {
        conversation = prep.conversation;
        client = prep.client;
        user = client.loggedOnUser;
    });
    
    it('function: changeConversationPin', async () => {
        const res = await client.changeConversationPin(conversation.convId);
        assert(res && res.conversationCreatorId === user.userId && res.pin && res.link);
    });

    it('functions: [disableGuestAccess, getConversationById]', async () => {
        await client.disableGuestAccess(conversation.convId);
        const conv = await client.getConversationById(conversation.convId);
        assert(conv.isGuestAccessDisabled);
    });

    it('functions: [enableGuestAccess, getConversationById]', async () => {
        await client.enableGuestAccess(conversation.convId);
        const conv = await client.getConversationById(conversation.convId);
        assert(!conv.isGuestAccessDisabled);
    });
});

<|start_filename|>test/browser/config.js<|end_filename|>
export default {
    config: {
        domain: 'circuitsandbox.net',
        client_id: '3dff321a3506404091938b8e2d384922'
    },
    credentials: {
        username: '<EMAIL>',
        password: '<PASSWORD>'
  }
}

<|start_filename|>NodeSDK.js<|end_filename|>
///////////////////////////////////////////////////////////////////////////////////////////////
// Global settings for Circuit Node.js
///////////////////////////////////////////////////////////////////////////////////////////////

/*eslint strict: ["error", "never"]*/

/**
 * Object defining Node.js SDK specific options.
 * @class NodeSDK
 */
var NodeSDK = {};

/**
 * If true, the server certificate is verified against the list of supplied CAs.
 * An error event is emitted if verification fails; err.code contains the OpenSSL
 * error code. Default: false.
 * Only applicable to Node.js SDK.
 * @property rejectUnauthorized
 * @type Boolean
 * @example
 *     NodeSDK.rejectUnauthorized = true;
 */
NodeSDK.rejectUnauthorized = false;

/**
 * HttpsProxyAgent object as defined in https://www.npmjs.com/package/https-proxy-agent.
 * Agent will be used on HTTP(S) request and WebSocket connection within Node SDK.
 * Only applicable to Node.js SDK.
 * @property proxyAgent
 * @type Object
 * @example
 *      // export http_proxy=http://172.20.1.100:8080
 *      var HttpsProxyAgent = require('https-proxy-agent');
 *      NodeSDK.proxyAgent = new HttpsProxyAgent(url.parse(process.env.http_proxy));
 */
NodeSDK.proxyAgent = undefined;

// Dummy logger. Overwritten by application using Circuit.setLogger
NodeSDK.logger = {
    debug: function () {},
    info: function () {},
    warning: function () {},
    warn: function () {},
    error: function () {}
};

module.exports = NodeSDK;




<|start_filename|>test/node/authentication.js<|end_filename|>
'use strict';

const assert = require('assert');
const Circuit = require('../../circuit-node');
const config = require('./config.json');
const prep = require('../preparation');
Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);

let client;
let client2;

describe('Authentication', () => {

    after(async  () => {
        // logs the bots back on for the remaining tests
        await prep.client.logon();
        await prep.client2.logon();
    });

    it('function: logon [user], with Client Credentials', async () => {
        client = new Circuit.Client(config.bot1);
        const user = await client.logon();
        assert(!!user);
    });

    it('function: isAuthenticated', async () => {
        await client.isAuthenticated();
    });

    /*
    it('function: validateToken', async () => {
        const token = await client.validateToken();
        assert(token.accessToken);
    });
    */

    it('function: logon [user2], with Client Credentials', async () => {
        client2 = new Circuit.Client(config.bot2);
        const user = await client2.logon();
        assert(!!user);
    });

    it('function: getCookie', async () => {
        const cookie = client.getCookie();
        assert(!!cookie);
    });
    
    it('asserts: client.domain', async () => {
        assert(client.domain === 'circuitsandbox.net');
    });

    it('asserts: client.accessToken', async () => {
        assert(!!(client.accessToken && client.accessToken.length));
    });

    it('asserts: client.expiresAt', async () => {
        assert(client.expiresAt === undefined || Number.isInteger(client.expiresAt));
    });

    it('function: logout', async () => {
        await client.logout(true);
        await client2.logout(true);
    });

    /*
    it('function: logon, with invalid credentials', async () => {
        client = new Circuit.Client({
            client_id: config.bot1.client_id,
            client_secret: config.bot2.client_secret
        });
        try {
            await client.logon();
            assert(false);
        } catch (err) {
            assert(true);
        }
    });
    */

    it('functions: [setOauthConfig, logon]', async () => {
        const client = new Circuit.Client();
        client.setOauthConfig(config.bot1);
        const user = await client.logon();
        assert(!!user);
        await client.logout();
    });

    it('functions: [logon, logout], with accessToken and accessToken property is cleared after logout', async () => {
        let client = new Circuit.Client(config.bot1);
        await client.logon();
        const token = client.accessToken;
        await client.logout();

        client = new Circuit.Client({
            client_id: config.bot1.client_id
        });
        const user = await client.logon({accessToken: token});
        assert(!!user);
        await client.logout();
        assert(!client.accessToken);
    });

    /*
    it('functions: [logon, logout], with failure to login after revoke of token', async () => {
        let client = new Circuit.Client(config.bot1);
        await client.logon();
        const token = client.accessToken;
        await client.revokeToken();
        await client.logout();

        client = new Circuit.Client({client_id: config.bot1.client_id});

        try {
            await client.logon({accessToken: token});
            assert(false);
        } catch (err) {
            assert(true);
        }
    });

    it('function: logon, with fresh after revoke of token', async () => {
        const client = new Circuit.Client(config.bot1);
        const user = await client.logon();
        assert(!!user);
        await client.logout();
    });


    it('functions: [logon, renewToken, logout], with event:  accessTokenRenewed and unable to login with old token', async () => {
        let client = new Circuit.Client(config.bot1);
        await client.logon();
        const token = client.accessToken;
        //helper.logEvents(client, Circuit.supportedEvents);
        const newToken = await client.renewToken();
        await helper.expectEvents(client, ['accessTokenRenewed']);
        assert(newToken && (newToken !== token));

        client = new Circuit.Client({
            client_id: config.bot1.client_id
        });
        try {
            await client.logon({accessToken: token});
            assert(false);
        } catch (err) {
            assert(true);
        }

        await client.logon({accessToken: newToken});
        await client.logout();
    });

    it('functions: [logon, renewSessionToken], with event: sessionTokenRenewed', async () => {
        let client = new Circuit.Client(config.bot1);
        await client.logon();
        const token = client.accessToken;
        await client.renewSessionToken();
        await helper.expectEvents(client, ['sessionTokenRenewed']);
    });
    */

});

<|start_filename|>FileUpload.js<|end_filename|>
///////////////////////////////////////////////////////////////////////////////////////////////
// FileUpload for Circuit Node.js
///////////////////////////////////////////////////////////////////////////////////////////////

var nodeUrl = require('url');
var fs = require('fs');
var https = require('https');
var NodeSDK = require('./NodeSDK');

module.exports = function FileUpload(config) {
    'use strict';

    var MAX_FILE_COUNT = 10;
    var MAX_FILE_SIZE = 100000000;

    var _stat = 0;

    var _logger = NodeSDK.logger;
    var _config = config || {};

    function isFileOK(file) {
        var error = null;

        if (file.path) {
            if (!fs.existsSync(file.path)) {
                error = new Error('[FileUpload] file not found');
            }
            _stat = fs.statSync(file.path);
            if (_stat.size > MAX_FILE_SIZE) {
                error = new Error('[FileUpload] max file size exceeded');
            }
        } else if (file.buffer) {
            _stat = { size: file.buffer.length };
            if (_stat.size > MAX_FILE_SIZE) {
                error = new Error('[FileUpload] max file size exceeded');
            }
        } else {
            error = new Error('[FileUpload] no file provided');
        }
        if (file.type === 'application/json') {
            _logger.debug('[FileUpload]: replace file type ', file.type);
            file.type = 'text/plain'; //upload json files as text files
        }
        return error;
    }

    function urlToOptions(url, file, stat, opts) {
        _logger.debug('[FileUpload] urlToOptions', url, file, stat);
        var parts = nodeUrl.parse(url);
        var options = {
            rejectUnauthorized: NodeSDK.rejectUnauthorized,
            agent: NodeSDK.proxyAgent,
            host: parts.hostname,
            port: parts.port ? parts.port : '443',
            path: parts.path,
            method: 'POST',
            headers: {
                'User-Agent': navigator.userAgent,
                'Cookie': _config.cookie,
                'Content-Disposition': 'attachment; filename="' + file.name + '"',
                'Content-Length': stat.size,
                'Content-Type': file.type
            }
        };

        if (opts) {
            opts.convId && (options.headers['x-conversation'] = opts.convId);
            opts.rtcSessionId && (options.headers['x-rtcsession'] = opts.rtcSessionId);
        }

        _logger.debug('[FileUpload] options', options);
        return options;
    }

    function uploadFile(file, url, opts) {
        return new Promise(function (resolve, reject) {
            var error = isFileOK(file);
            if (error) {
                reject(error);
                return;
            }
            var options = urlToOptions(url, file, _stat, opts);
            var req = https.request(options, function (res) {
                if (!res) {
                    _logger.error('[FileUpload]: no res in https callback');
                    error = new Error('[FileUpload]: no res in https callback');
                    reject(error);
                }
                _logger.debug('[FileUpload]: https callback', res.statusCode, res.statusMessage, res.headers);
                if (res.statusCode !== 200) {
                    _logger.error('[FileUpload]: status code: ' + res.statusCode, res.statusMessage);
                    error = new Error('[FileUpload]: status code ' + res.statusCode, res.statusMessage);
                    reject(error);
                }
                var body = '';
                res.on('data', function (d) {
                    _logger.debug('[FileUpload]: result on data', d);
                    body += d;
                });
                res.on('end', function () {
                    _logger.debug('[FileUpload]: result on end', body.toString('utf8'));
                    resolve(body);
                });
                res.on('error', function (e) {
                    _logger.debug('[FileUpload]: result on error', e);
                    reject(e);
                });
            });

            if (file.path) {
                var readStream = fs.createReadStream(file.path);
                readStream.on('data', function (chunk) {
                    _logger.debug('[FileUpload]: readStream on data', file.path, chunk);
                    req.write(chunk);
                });
                readStream.on('end', function () {
                    _logger.debug('[FileUpload]: readStream on end ', file.path);
                    req.end();
                });

                req.on('error', function (e) {
                    _logger.debug('[FileUpload]: request on error', file.path, e);
                    reject(e);
                });
            } else if (file.buffer) {
                req.end(file.buffer);
            }
        });
    }

    this.uploadFiles = function (files, domain) {
        domain = domain || _config.domain;
        _logger.debug('[FileUpload] node SDK uploadFiles: ', files, domain);

        if (files.length > MAX_FILE_COUNT) {
            return Promise.reject('[FileUpload]: Exceeded maximum of %d% files per message', MAX_FILE_COUNT);
        }

        var result = [];

        // Sequentially (but async) upload the files. Once all files are
        // uploaded, resolve the Promise passing the upload results.
        var itemId = null;
        return files.reduce(function (sequence, file) {
            return sequence.then(function () {
                var url = 'https://' + domain + '/fileapi?itemid=' + (itemId || 'NULL');
                return uploadFile(file, url)
                .then(function (res) {
                    var resp = JSON.parse(res)[0];
                    itemId = itemId || resp.id;
                    var attachmentMetaData = {
                        fileId: resp.fileid.slice(-1)[0],
                        fileName: file.name,
                        itemId: itemId,
                        mimeType: resp.mimeType || file.type,
                        size: file.size
                    };
                    result.push(attachmentMetaData);
                    return result;
                });
            });
        }, Promise.resolve());
    };

    this.uploadWhiteboardFile = function (file, rtcSessionId) {
        _logger.debug('[FileUpload] node SDK uploadWhiteboardFile: ', file);

        var url = 'https://' + _config.domain + '/fileapi';
        var opts = {
            rtcSessionId: rtcSessionId
        };

        return uploadFile(file, url, opts)
        .then(function (res) {
            var resp = JSON.parse(res)[0];
            var fileUrl = url + '?fileid=' + resp.id;
            var fileName = file.name;
            var attachmentMetaData = {
                fileId: resp.id,
                fileName: fileName,
                mimeType: resp.mimeType || file.type,
                size: file.size,
                url: fileUrl,
                deleteUrl: fileUrl
            };
            return attachmentMetaData;
        });
    };
};


<|start_filename|>test/node/conversation-items.js<|end_filename|>
'use strict';

const assert = require('assert');
const Circuit = require('../../circuit-node');
const helper = require('./helper');
const prep = require('../preparation');
Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);

let client;
let user;
let client2;
let user2;
let item = {};
let conversation;
describe('Conversation Items', () => {
    before(async () => {
        conversation = prep.conversation;
        client = prep.client;
        user = client.loggedOnUser;
        client2 = prep.client2;
        user2 = client2.loggedOnUser;
    });

    it('function: addTextItem, with event: itemAdded', async () => {
        const textValue = `${Date.now()}a`;
        const res  = await Promise.all([
            client.addTextItem(conversation.convId, textValue),
            helper.expectEvents(client, [{
                type: 'itemAdded',
                predicate: evt => evt.item.convId === conversation.convId
            }])           
        ]);
        item = res[0];
        assert(item.convId === conversation.convId && item.text.content === textValue);
    });
    
    it('function: updateTextItem, with event: itemUpdated', async () => {
        const textValue = `${Date.now()}b`;
        const subject = `${Date.now()}c`;
        const content = {
            itemId: item.itemId,
            subject: subject,
            content: textValue,     
        }
        const res  = await Promise.all([
            client.updateTextItem(content),
            helper.expectEvents(client, [{
                type: 'itemUpdated',
                predicate: evt => evt.item.convId === conversation.convId && evt.item.itemId === item.itemId
            }])           
        ]);
        item = res[0];
        assert(item.itemId === content.itemId && item.text.content === textValue && item.text.subject === subject);
    });

    it('functions: [addTextItem, getItemsByThread]', async () => {
        const content = {
            parentId: item.itemId,
            content: `${Date.now()}item2`
        }
        const item2 = await client.addTextItem(conversation.convId, content);
        const res = await client.getItemsByThread(conversation.convId, item.itemId);
        assert(res && res.items.some(threadItem => threadItem.parentItemId === item.itemId && threadItem.itemId === item2.itemId));
    });

    it('function: getConversationFeed', async () => {
        const res  = await client.getConversationFeed(conversation.convId);
        assert(res && res.threads.some(thread => thread.parentItem.convId === conversation.convId && thread.parentItem.itemid === item.itemid));
    });

    it('function: getConversationItems,  with {direction: AFTER}', async () => {
        const options = {   
            creationDate: item.creationTime - 1,
            direction: 'AFTER'
        }
        const res  = await client.getConversationItems(conversation.convId, options);
        assert(res.some(conversationItem => conversationItem.itemId === item.itemId));
    }); 

    it('functions: [flagItem, getFlaggedItems], with event: itemFlagged', async () => {
        if (!Circuit.supportedEvents.includes('itemFlagged')) {
            console.log('Event not supported.');
            assert(true);
            return;
        }
        await Promise.all([
            client.flagItem(conversation.convId, item.itemId),
            helper.expectEvents(client, [{
                type: 'itemFlagged',
                predicate: evt => evt.convId === conversation.convId && evt.itemId === item.itemId
            }])         
        ]);
        const res = await client.getFlaggedItems();
        assert(res && res.some(conv => conv.conversationId === conversation.convId && conv.conversationItemData.some(i => i.itemId === item.itemId)));
    });
    
    it('functions: [unflagItem, getFlaggedItems], with event: itemUnflagged', async () => {
        if (!Circuit.supportedEvents.includes('itemUnflagged')) {
            console.log('Event not supported.');
            assert(true);
            return;
        }
        await Promise.all([
            client.unflagItem(conversation.convId, item.itemId),
            helper.expectEvents(client, [{
                type: 'itemUnflagged',
                predicate: evt => evt.convId === conversation.convId && evt.itemId === item.itemId
            }])         
        ]);
        const res = await client.getFlaggedItems();      
        assert(res && !res.some(conv => conv.conversationId === conversation.convId && conv.conversationItemData.some(i => i.itemId === item.itemId)));
    });

    it('functions: [likeItem, getItemById], with event: itemUpdated', async () => {
        await Promise.all([
            client.likeItem(item.itemId),
            helper.expectEvents(client, [{
                type: 'itemUpdated',
                predicate: evt => evt.item.itemId === item.itemId && evt.item.convId === conversation.convId
            }]) 
        ]);
        const res = await client.getItemById(item.itemId);
        assert(res.text.likedByUsers.includes(user.userId));
    });

    it('functions: [unlikeItem, getItemById], with event: itemUpdated', async () => {
        await Promise.all([
            client.unlikeItem(item.itemId),
            helper.expectEvents(client, [{
                type: 'itemUpdated',
                predicate: evt => evt.item.itemId === item.itemId && evt.item.convId === conversation.convId
            }]) 
        ]);
        const res = await client.getItemById(item.itemId);
        assert(!res.text.likedByUsers || !res.text.likedByUsers.includes(user.userId));
    });

    it('function: markItemsAsRead, with event: conversationReadItems', async () => {
        const res = await Promise.all([
            client.markItemsAsRead(conversation.convId),
            helper.expectEvents(client, [{
                type: 'conversationReadItems',
                predicate: evt => evt.data.convId === conversation.convId
            }]) 
        ]);
        assert(res[1].data.convId === conversation.convId);
    });

    it('functions: [updateUser, addTextItem], with event: mention', async () => {
        await client2.updateUser({
            userId: user2.userId,
            firstName: 'John',
            lastName: 'Smith'
        });
        user2 = await client2.getUserById(user2.userId);
        const content = `<span class="mention" abbr="${user2.userId}">@${user2.displayName}</span>`;
        Circuit.Enums.TextItemContentType.RICH;
        const res  = await Promise.all([
            client.addTextItem(conversation.convId, {
                content: content,
                contentType: Circuit.Enums.TextItemContentType.RICH
            }),
            helper.expectEvents(client2, [{
                type: 'mention',
                predicate: evt => evt.mention.userReference.userId === user.userId && evt.mention.itemReference.convId === conversation.convId
            }]) 
        ]);
        await client2.updateUser({
            userId: user2.userId,
            firstName: user2.firstName,
            lastName: user2.lastName
        });
        const mentionedItem = res[0];
        const mention = res[1].mention;
        assert(mentionedItem.convId === conversation.convId && mentionedItem.creatorId === user.userId && mentionedItem.itemId === mention.itemReference.itemId && mention.userReference.userId === user.userId);
    });
});


<|start_filename|>test/node/conversation-form.js<|end_filename|>
'use strict';

const assert = require('assert');
const Circuit = require('../../circuit-node');
const config = require('./config.json');
const helper = require('./helper');
const prep = require('../preparation');
Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);

let client;
let client2;
let conversation;
let item;
describe('Conversation Form', () => {
    before(async () => {
        conversation = prep.conversation;
        client = prep.client;
        client2 = prep.client2;
    });

    it('function: addTextItem, with form', async () => {
        const form = {
            title: `${Date.now()}title`, // optional
            id: `${Date.now()}id`,
            controls: [{
                type: 'RADIO',
                name: 'radioButton',
                defaultValue: '1', // optional
                options: [{
                    text: 'button1',
                    value: '1'
                },{
                text: 'button2',
                value: '2'
                },{
                text: 'button3',
                value: '3'
                }]
            },{
                type: 'CHECKBOX',
                name: 'checkBox',
                options: [{ // required for multi select
                    text: 'check1',
                    value: '1',
                    defaultValue: 'true'
                }, {
                text: 'check2',
                value: '2',
                }, {
                text: 'check3',
                value: '3',
                }]
            },{
                type: 'DROPDOWN',
                name: 'dropDown',
                defaultValue: '3', // optional
                options: [{
                    text: 'dropDown1',
                    value: '1',
                },{
                text: 'dropDown2',
                value: '2',
                },{
                text: 'dropDown3',
                value: '3',
                }]
            },{
                type: 'INPUT',
                name: 'inputField',
                text: 'Input field',
            },{
                type: 'SPACER'
            },{
                type: 'LABEL',
                text: 'Label'
            },{
                type: 'BUTTON', // submit the form
                options: [{
                    text: 'Submit',
                    action: 'submit',
                    notification: 'Form submitted successfully'
                },{
                text: 'Reset',
                action: 'reset',
                notification: 'Form reset successfully'
                }]
            }]
        }
        const content = {
            content: `${Date.now()}a`,
            form: form
          }
        item = await client.addTextItem(conversation.convId, content);
        assert(item.text.formMetaData && item.text.formMetaData.id === form.id && item.text.formMetaData.title === form.title && item.text.formMetaData.controls.length === form.controls.length);
    });

    it('function: submitForm, with event: formSubmission', async () => {
        let dataValues = {}
        const submitFormData = {
            id: item.text.formMetaData.id,
            data: [{
                name: 'radioButton',
                value: '2'
               },{
                name: 'checkBox',
                value: '3'
               },{
                name: 'dropDown',
                value: '1'
               },{
                name: 'inputField',
                value: `${Date.now()}input`
               }
            ]
          }
        submitFormData.data.forEach(entry => dataValues[entry.name] = entry);
        const res  = await Promise.all([
            client2.submitForm(item.itemId, submitFormData),
            helper.expectEvents(client, [{
                type: 'formSubmission',
                predicate: evt => evt.itemId === item.itemId &&  evt.form.id === item.text.formMetaData.id && evt.form.data.every(entry => dataValues[entry.name] && dataValues[entry.name].value === entry.value)
            }])
        ]);
        assert(res[1].itemId === item.itemId &&  res[1].form.id === item.text.formMetaData.id && res[1].form.data.every(entry => dataValues[entry.name] && dataValues[entry.name].value === entry.value));
    });
});

<|start_filename|>test/index.js<|end_filename|>
const runner = require('./runner');
const express = require('express');
const serveStatic = require('serve-static');
const app = express();
const argv = require('minimist')(process.argv.slice(2));

// Default options
let options = {
    files: 'test/browser/*.html', // string or array. supports glob
    port: 3000,
    width: 900,
    height: 600,
    timeout: 120 * 1000,
    //  executablePath: '/usr/bin/chrome-unstable',  // chrome executable path
    visible: false,
    args: [
        'no-sandbox',
        'use-fake-device-for-media-stream',
        'use-fake-ui-for-media-stream',
        'mute-audio'
    ]
};

options = Object.assign(options, argv);

app.use(serveStatic('.'));
app.listen(options.port);

runner(options)
    .then(results => results.find(res => res.failures.length) ? 1 : 0)
    .catch(() => 1)
    .then(process.exit);



<|start_filename|>test/browser/call-muting.js<|end_filename|>
'use strict';

import { PeerUser } from '../peer-user.js';
import { expectEvents, sleep } from '../helper.js';
import config from './config.js'

const assert = chai.assert;
let client;
let peerUser1, peerUser2;
let call;
describe('Call Muting', async function() {
    this.timeout(300000);

    before(async function() {
        Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);
        client = new Circuit.Client(config.config);
        const res = await Promise.all([PeerUser.create(), PeerUser.create(), client.logon(config.credentials)]);
        peerUser1 = res[0];
        peerUser2 = res[1];
        const conversation = await client.createGroupConversation([peerUser1.userId, peerUser2.userId], 'SDK Test: Call Muting');
        call = await client.startConference(conversation.convId, {audio: true, video: false});
        await expectEvents(client, [{
            type: 'callStatus',
            predicate: evt => evt.call.state === Circuit.Enums.CallStateName.Initiated
        }, {
            type: 'callStatus',
            predicate: evt => evt.call.state === Circuit.Enums.CallStateName.Waiting
        }]);
        await sleep(5000); // wait to make sure the call is ready to be joined
        await Promise.all([
            peerUser1.exec('joinConference', call.callId, {audio: true, video: false}),
            peerUser2.exec('joinConference', call.callId, {audio: true, video: false}),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'callStateChanged' && evt.call.state === Circuit.Enums.CallStateName.Active
            }, {
                type: 'callStatus',
                predicate: evt => evt.reason === 'participantJoined'
            }])
        ]);
        call = await client.findCall(call.callId);
        await sleep(5000);
    });

    after(async function() {
        await Promise.all([peerUser1.destroy(), peerUser2.destroy(), client.logout()]);
    });

    afterEach(async function() {
        client.removeAllListeners();
    });

    it('function: muteParticipant, raises event: callStatus with reason: participantUpdated', async () => {
        await Promise.all([
            client.muteParticipant(call.callId, peerUser2.userId),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'participantUpdated' && evt.call.callId === call.callId && evt.participant.muted
            }])
        ]);
    });

    it('function: mute, raises event: callStatus with reason: localUserSelfMuted', async () => {
        await Promise.all([
            client.mute(call.callId),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'localUserSelfMuted' && evt.call.callId === call.callId && evt.call.locallyMuted
            }])
        ]);
    });

    it('function: unmute, raises event: callStatus with reason: localUserSelfUnmuted', async () => {
        await Promise.all([
            client.unmute(call.callId),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'localUserSelfUnmuted' && evt.call.callId === call.callId && !evt.call.locallyMuted
            }])
        ]);
    });

    it('function: muteRtcSession, raises event: callStatus with reason: participantUpdated', async () => {
        await Promise.all([
            client.muteRtcSession(call.callId),
            expectEvents(client, [{
                type: 'callStatus',
                predicate: evt => evt.reason === 'participantUpdated' && evt.call.callId === call.callId && evt.participant.muted
            }])
        ]);
        await sleep(3000); // wait 3 seconds to allow the other users to be muted
        call = await client.findCall(call.callId);
        assert(call.participants.every(user => user.muted));
    });
});


<|start_filename|>test/node/conversation-direct.js<|end_filename|>
'use strict';

const assert = require('assert');
const Circuit = require('../../circuit-node');
const prep = require('../preparation');
Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);

let client;
let user;
let client2;
let user2;
let conversation;
describe('Conversation Direct', () => {
    before(async () => {
        client = prep.client;
        user = client.loggedOnUser;
        client2 = prep.client2;
        user2 = client2.loggedOnUser;
    });

    it('function: createDirectConversation', async () => {
        const res = await client.createDirectConversation(user2.userId);
        conversation = res.conversation;
        assert(conversation.participants.includes(user.userId) && conversation.participants.includes(user2.userId));
    });

    it('function: getDirectConversationWithUser', async () => {
        const res = await client.getDirectConversationWithUser(user2.emailAddress);
        assert(res && res.convId === conversation.convId && res.participants.includes(user.userId) && res.participants.includes(user2.userId));
    });

    it('function: getConversationDetails', async () => {
        const res = await client.getConversationDetails(conversation.convId);
        assert(res.conversationCreatorId === user.userId || res.conversationCreatorId === user2.userId);
    });
});

<|start_filename|>test/node/miscellaneous.js<|end_filename|>
'use strict';

const assert = require('assert');
const Circuit = require('../../circuit-node');
const prep = require('../preparation');
Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);

let client;
describe('Miscellaneous Tests', () => {
    before(async () => {
        client = prep.client;
    });

    it('function: getSupportConversationId', async () => {
        await client.getSupportConversationId();
    });

    it('function: getTelephonyConversationId', async () => {
        const res = await client.getTelephonyConversationId();
        assert(res);
    });

    it('function: getTelephonyData', async () => {
        const res = await client.getTelephonyData();
        assert(res);
    });
});

<|start_filename|>test/node/conversation-favorite.js<|end_filename|>
'use strict';

const assert = require('assert');
const Circuit = require('../../circuit-node');
const helper = require('./helper');
const prep = require('../preparation');
Circuit.logger.setLevel(Circuit.Enums.LogLevel.Error);

let client;
let conversation;
describe('Conversation Favorites', () => {
    before(async () => {
        conversation = prep.conversation;
        client = prep.client;
    });

    it('functions: [favoriteConversation, getFavoriteConversationIds], with event: conversationFavorited', async () => {
        await Promise.all([
            client.favoriteConversation(conversation.convId),
            helper.expectEvents(client, [{
                type: 'conversationFavorited',
                predicate: evt => evt.convId === conversation.convId 
            }])            
        ]);
        const res = await client.getFavoriteConversationIds();
        assert(res && res.includes(conversation.convId));
    });

    it('function: getMarkedConversations', async () => {
        const res = await client.getMarkedConversations();
        assert(res && res.favoriteConvIds.some(convId => convId === conversation.convId));
    });

    it('functions: [unfavoriteConversation, getFavoriteConversationIds], with event: conversationUnfavorited', async () => {
        await Promise.all([
            client.unfavoriteConversation(conversation.convId),
            helper.expectEvents(client, [{
                type: 'conversationUnfavorited',
                predicate: evt => evt.convId === conversation.convId 
            }])            
        ]);
        const res = await client.getFavoriteConversationIds();
        assert(res && !res.includes(conversation.convId));
    });  
});