UNPKG

@laboralphy/o876-txat

Version:

An embeddable chat library, multi channels, moderation, event based

466 lines (387 loc) 17.7 kB
const StdDateProvider = require('../src/date-provider/StdDateProvider'); const IDateProvider = require('../src/date-provider/IDateProvider'); const EVENT_TYPES = require('../src/data/event-types.json'); const TxatManager = require('../src/TxatManager'); const User = require('../src/User'); const Channel = require('../src/Channel'); const USER_RANKS = require('../src/data/user-ranks.json'); describe('channelExists', () => { it('should return false when asking for a non existant channel', () => { const tm = new TxatManager(); expect(tm.channelExists('wtf')).toBeFalsy(); }); it('should return true when asking for an existant channel', () => { const tm = new TxatManager(); tm.createChannel('xyz'); expect(tm.channelExists('xyz')).toBeTruthy(); }); }); describe('userExists', () => { it('should return true when asking for an non-existant user', () => { const tm = new TxatManager(); expect(tm.userExists('xyz')).toBeFalsy(); }); it('should return true when asking for an existant user', () => { const tm = new TxatManager(); tm.connectUser('zerty'); expect(tm.userExists('zerty')).toBeTruthy(); }); it('should return false when asking for an existant user who has just disconnected', () => { const tm = new TxatManager(); const u = tm.connectUser('zerty'); expect(tm.userExists('zerty')).toBeTruthy(); tm.disconnectUser(u); expect(tm.userExists('zerty')).toBeFalsy(); }); }); describe('channelList', () => { it('should return [abc, def] when creating two channels abc & def', function () { const tm = new TxatManager(); tm.createChannel('abc'); tm.createChannel('def'); expect(tm.getChannelList().map(c => c.id)).toEqual(['abc', 'def']); }); it('should return [abc] when creating two channels abc & def, and destroying abc', function () { const tm = new TxatManager(); tm.createChannel('def'); tm.createChannel('abc'); tm.destroyChannel('def'); expect(tm.getChannelList().map(c => c.id)).toEqual(['abc']); }); }); describe('createUser', () => { it('should create user instance when calling method', function () { const tm = new TxatManager(); const u = tm.createUser('aaa'); expect(u).toBeInstanceOf(User); }); it('should *NOT* add created user to user list', function () { const tm = new TxatManager(); tm.createUser('aaa'); expect(tm.userExists('aaa')).toBeFalsy(); }); }); describe('getChannel', () => { it('should return newly created channel', function () { const tm = new TxatManager(); const c = tm.createChannel('abc'); const d2 = tm.getChannel('abc'); expect(c).toBeInstanceOf(Channel); expect(d2).toBeInstanceOf(Channel); expect(c).toBe(d2); }); }); describe('getUser', () => { it('should not return newly created user prior to connecting', () => { const tm = new TxatManager(); tm.createUser('aaa'); expect(() => tm.getUser('aaa')).toThrow('user aaa does not exist'); }); it('should return newly created user after connecting', () => { const tm = new TxatManager(); expect(() => tm.getUser('aaa')).toThrow('user aaa does not exist'); const u = tm.connectUser('aaa'); expect(() => tm.getUser('aaa')).not.toThrow('user aaa does not exist'); expect(tm.getUser('aaa')).toBe(u); }); }); describe('connectUser', () => { it('should re use previous connected instance of user', () => { const tm = new TxatManager(); expect(() => tm.getUser('aaa')).toThrow('user aaa does not exist'); const u = tm.connectUser('aaa'); expect(tm.getUser('aaa')).toBe(u); tm.disconnectUser(u); expect(() => tm.getUser('aaa')).not.toThrow('user aaa does not exist'); expect(u.connected).toBe(false); const u2 = tm.connectUser('aaa'); expect(tm.getUser('aaa')).toBe(u); expect(tm.getUser('aaa')).toBe(u2); expect(u.connected).toBe(true); }); }); describe('userJoinsChannel', () => { it('should make a user join a new channel', function () { const tm = new TxatManager(); const aEvents = []; tm.events.on(EVENT_TYPES.EVENT_USER_CONNECTED, ({ user }) => { aEvents.push({ type: EVENT_TYPES.EVENT_USER_CONNECTED, user }); }); tm.events.on(EVENT_TYPES.EVENT_USER_JOINED_CHANNEL, ({ user }) => { aEvents.push({ type: EVENT_TYPES.EVENT_USER_JOINED_CHANNEL, user }); }); const u = tm.connectUser('u1'); tm.userJoinsChannel(u, 'lobby'); expect(aEvents).toHaveLength(2); expect(aEvents[1].type).toBe(EVENT_TYPES.EVENT_USER_JOINED_CHANNEL); expect(aEvents[1].user.id).toBe('u1'); }); it('should fire event when new user join existing channel', function () { const tm = new TxatManager(); const aEvents = []; tm.events.on(EVENT_TYPES.EVENT_USER_JOINED_CHANNEL, ({ recipient, user, channel }) => { aEvents.push({ type: EVENT_TYPES.EVENT_USER_JOINED_CHANNEL, recipient, user, channel }); }); const u1 = tm.createUser('u1'); const u2 = tm.createUser('u2'); expect(aEvents).toHaveLength(0); tm.userJoinsChannel(u1, 'lobby'); expect(aEvents).toHaveLength(1); expect(aEvents[0].user.id).toBe('u1'); expect(aEvents[0].recipient).toBe('u1'); tm.userJoinsChannel(u2, 'lobby'); expect(aEvents).toHaveLength(3); expect(aEvents[1].user.id).toBe('u2'); expect(aEvents[1].recipient).toBe('u1'); expect(aEvents[2].user.id).toBe('u2'); expect(aEvents[2].recipient).toBe('u2'); }); }); describe('userLeavesChannel', () => { it('should throw an error when leaving a channel you are not in', () => { const tm = new TxatManager(); const u1 = tm.connectUser('u1'); const u2 = tm.connectUser('u2'); tm.userJoinsChannel(u1, 'lobby'); expect(() => tm.userLeavesChannel(u2, tm.getChannel('lobby'))).toThrow(`can't remove user ${u2.id} from channel : not connected to this channel`); }); it('should destroy channel lobby when all users leave', () => { const tm = new TxatManager(); const u1 = tm.connectUser('u1'); const u2 = tm.connectUser('u2'); tm.userJoinsChannel(u1, 'lobby'); tm.userJoinsChannel(u2, 'lobby'); expect(tm.channelExists('lobby')).toBe(true); const c = tm.getChannel('lobby'); tm.userLeavesChannel(u1, c); expect(tm.channelExists('lobby')).toBe(true); tm.userLeavesChannel(u2, c); expect(tm.channelExists('lobby')).toBe(false); }); }); describe('userSendsChannelMessage', () => { it ('should send public message to all channel users', () => { const tm = new TxatManager(); const logMessages = []; tm.events.on(EVENT_TYPES.EVENT_CHANNEL_MESSAGE, ({ recipient, channel, message }) => { logMessages.push({ user: recipient, channel: channel.id, messageSender: message.sender.id, messageContent: message.content }); }); const u1 = tm.connectUser('u1'); const u2 = tm.connectUser('u2'); const u3 = tm.connectUser('u3'); const u4 = tm.connectUser('u4'); tm.userJoinsChannel(u1, 'lobby'); tm.userJoinsChannel(u2, 'lobby'); tm.userJoinsChannel(u3, 'lobby'); const lobby = tm.getChannel('lobby'); tm.userSendsChannelMessage(u2, lobby, 'hello!'); expect(logMessages.length).toBe(3); expect(logMessages[0].messageSender).toBe('u2'); expect(logMessages[1].messageSender).toBe('u2'); expect(logMessages[2].messageSender).toBe('u2'); expect(logMessages[0].channel).toBe('lobby'); expect(logMessages[1].channel).toBe('lobby'); expect(logMessages[2].channel).toBe('lobby'); expect(logMessages[0].messageContent).toBe('hello!'); expect(logMessages[1].messageContent).toBe('hello!'); expect(logMessages[2].messageContent).toBe('hello!'); const recipients = new Set(); logMessages.forEach(m => recipients.add(m.user)); expect(recipients.has('u1')).toBeTruthy(); expect(recipients.has('u2')).toBeTruthy(); expect(recipients.has('u3')).toBeTruthy(); tm.userLeavesChannel(u2, lobby); logMessages.splice(0, 3); expect(logMessages.length).toBe(0); tm.userSendsChannelMessage(u1, lobby, 'agur!'); expect(logMessages.length).toBe(2); expect(logMessages[0].messageSender).toBe('u1'); expect(logMessages[1].messageSender).toBe('u1'); expect(logMessages[0].channel).toBe('lobby'); expect(logMessages[1].channel).toBe('lobby'); expect(logMessages[0].messageContent).toBe('agur!'); expect(logMessages[1].messageContent).toBe('agur!'); const recipients2 = new Set(); logMessages.forEach(m => recipients2.add(m.user)); expect(recipients2.has('u1')).toBeTruthy(); expect(recipients2.has('u3')).toBeTruthy(); logMessages.splice(0, logMessages.length); expect(logMessages.length).toBe(0); tm.userJoinsChannel(u4, 'lobby'); tm.userSendsChannelMessage(u4, lobby, 'hi!'); expect(logMessages.length).toBe(3); expect(logMessages[0].messageSender).toBe('u4'); expect(logMessages[1].messageSender).toBe('u4'); expect(logMessages[2].messageSender).toBe('u4'); expect(logMessages[0].channel).toBe('lobby'); expect(logMessages[1].channel).toBe('lobby'); expect(logMessages[2].channel).toBe('lobby'); expect(logMessages[0].messageContent).toBe('hi!'); expect(logMessages[1].messageContent).toBe('hi!'); expect(logMessages[2].messageContent).toBe('hi!'); const recipients3 = new Set(); logMessages.forEach(m => recipients3.add(m.user)); expect(recipients3.has('u1')).toBeTruthy(); expect(recipients3.has('u3')).toBeTruthy(); expect(recipients3.has('u4')).toBeTruthy(); }); it ('should not send public message when not connected to channel', () => { const tm = new TxatManager(); const u1 = tm.connectUser('u1'); const u2 = tm.connectUser('u2'); const u3 = tm.connectUser('u3'); tm.userJoinsChannel(u1, 'lobby'); tm.userJoinsChannel(u2, 'lobby'); const lobby = tm.getChannel('lobby'); expect(tm.userSendsChannelMessage(u2, lobby, 'agur!')).toBeTruthy(); expect(tm.userSendsChannelMessage(u1, lobby, 'xxx!')).toBeTruthy(); expect(() => tm.userSendsChannelMessage(u3, lobby, 'vvv')).toThrow('user u3 does not exist in this channel'); }); }); describe('userSendsPrivateMessage', () => { it('should produce event when sending private message', () => { const tm = new TxatManager(); const u1 = tm.connectUser('u1'); const u2 = tm.connectUser('u2'); const logPM = []; tm.events.on(EVENT_TYPES.EVENT_USER_RECEIVE_PRIVATE_MESSAGE, ({ recipient, message }) => { logPM.push({ from: message.sender.id, content: message.content, to: recipient }); }); tm.userSendsPrivateMessage(u1, u2, 'hello'); expect(logPM.length).toBe(1); expect(logPM[0]).toEqual({ from: 'u1', to: 'u2', content: 'hello' }); }); }); describe('setUserRank', () => { it('should throw an error when trying to raise own rank', function () { const tm = new TxatManager(); const u1 = tm.connectUser('u1'); const u2 = tm.connectUser('u2'); tm.userJoinsChannel(u1, 'lobby'); tm.userJoinsChannel(u2, 'lobby'); const lobby = tm.getChannel('lobby'); expect(() => tm.setUserRank(u1, lobby, u1, USER_RANKS.USER_RANK_ADMIN)) .toThrow('user rank is greater or equal than promoter rank ; user USER_RANK_HOST - promoter USER_RANK_HOST'); expect(() => tm.setUserRank(u2, lobby, u2, USER_RANKS.USER_RANK_ADMIN)) .toThrow('user u2 can\'t perform this operation on channel lobby ; insufficient capabilities ; need CAPABILITY_PROMOTE'); }); it('should throw an error when trying to raise user rank above own rank', function () { const tm = new TxatManager(); const u1 = tm.connectUser('u1'); const u2 = tm.connectUser('u2'); const u3 = tm.connectUser('u3'); tm.userJoinsChannel(u1, 'lobby'); tm.userJoinsChannel(u2, 'lobby'); tm.userJoinsChannel(u3, 'lobby'); const lobby = tm.getChannel('lobby'); expect(() => tm.setUserRank(u1, lobby, u1, USER_RANKS.USER_RANK_ADMIN)) .toThrow('user rank is greater or equal than promoter rank ; user USER_RANK_HOST - promoter USER_RANK_HOST'); lobby.setUserRank(u2, USER_RANKS.USER_RANK_ADMIN); expect(lobby.getUserRank(u2).id).toBe(USER_RANKS.USER_RANK_ADMIN); expect(() => tm.setUserRank(u2, lobby, u3, USER_RANKS.USER_RANK_HOST)) .toThrow('new rank would be higher than promoter rank ; promoter USER_RANK_ADMIN - target rank USER_RANK_HOST'); }); }); describe('ignoreUser / unignore', () => { it('should not send public message to u1 when u2 speaking and u1 ignoring u2', () => { const tm = new TxatManager(); const u1 = tm.connectUser('u1'); const u2 = tm.connectUser('u2'); const lobby = tm.createChannel('lobby'); lobby.permanent = true; lobby.public = true; const aLogMessages = []; tm.events.on(EVENT_TYPES.EVENT_CHANNEL_MESSAGE, ({ recipient }) => { aLogMessages.push(recipient); }); tm.userJoinsChannel(u1, lobby); tm.userJoinsChannel(u2, lobby); expect(lobby.users.size).toBe(2); tm.userSendsChannelMessage(u2, lobby, 'u1 is stupid'); expect(aLogMessages.includes('u1')).toBeTruthy(); expect(aLogMessages.includes('u2')).toBeTruthy(); tm.ignoreUser(u1, u2); aLogMessages.splice(0, aLogMessages.length); tm.userSendsChannelMessage(u2, lobby, 'u1 is a complete moron'); expect(aLogMessages.includes('u1')).toBeFalsy(); expect(aLogMessages.includes('u2')).toBeTruthy(); tm.unignoreUser(u1, u2); aLogMessages.splice(0, aLogMessages.length); tm.userSendsChannelMessage(u2, lobby, 'u1 is still a complete moron'); expect(aLogMessages.includes('u1')).toBeTruthy(); expect(aLogMessages.includes('u2')).toBeTruthy(); }); it('should not receive private message from u2 when ignoring u2', () => { const tm = new TxatManager(); const u1 = tm.connectUser('u1'); const u2 = tm.connectUser('u2'); const lobby = tm.createChannel('lobby'); lobby.permanent = true; lobby.public = true; const aLogMessages = []; tm.events.on(EVENT_TYPES.EVENT_USER_RECEIVE_PRIVATE_MESSAGE, ({ message }) => { aLogMessages.push(message.sender.id + ': ' + message.content); }); tm.userJoinsChannel(u1, lobby); tm.userJoinsChannel(u2, lobby); tm.userSendsPrivateMessage(u2, u1, 'tu es un fdp'); expect(aLogMessages.length).toBe(1); expect(aLogMessages[0]).toBe('u2: tu es un fdp'); tm.ignoreUser(u1, u2); tm.userSendsPrivateMessage(u2, u1, 'tu es un GROS fdp'); expect(aLogMessages.length).toBe(1); expect(aLogMessages[0]).toBe('u2: tu es un fdp'); tm.unignoreUser(u1, u2); tm.userSendsPrivateMessage(u2, u1, 'tu es un GIGA fdp'); expect(aLogMessages.length).toBe(2); expect(aLogMessages[1]).toBe('u2: tu es un GIGA fdp'); }); }); describe('information channel', function () { it ('should not be able to talk in this channel, only receive message, when chan default user rank is set to VIEWER', () => { const tm = new TxatManager(); const aLog = []; tm.events.on(EVENT_TYPES.EVENT_CHANNEL_MESSAGE, ({ recipient, channel, message }) => { if (tm.getUser(recipient).data.system) { return; } aLog.push({ recipient, channel: channel.id, message: message.sender.name + ': ' + message.content }); }); const u1 = tm.connectUser('u1'); const usys = tm.connectUser('system'); usys.data.system = true; usys.name = 'System'; tm.userJoinsChannel(usys, 'info'); const infoChan = tm.getChannel('info'); infoChan.public = true; infoChan.permanent = true; infoChan.defaultUserRank = USER_RANKS.USER_RANK_VIEWER; tm.userJoinsChannel(u1, infoChan); expect(aLog.length).toBe(0); tm.userSendsChannelMessage(usys, infoChan, 'les utilisateurs sont des fdp'); expect(aLog.length).toBe(1); expect(aLog[0].channel).toBe('info'); expect(aLog[0].message).toBe('System: les utilisateurs sont des fdp'); tm.userSendsChannelMessage(u1, infoChan, 'moi aussi je dis : les utilisateur sont des fdp'); expect(aLog.length).toBe(1); expect(aLog[0].channel).toBe('info'); expect(aLog[0].message).toBe('System: les utilisateurs sont des fdp'); }); });