2019-04-23 11:50:08 +00:00
|
|
|
'use strict'
|
|
|
|
|
|
|
|
/* eslint-env node, mocha */
|
|
|
|
|
|
|
|
const mock = require('mock-require')
|
|
|
|
const assert = require('assert')
|
2019-05-24 04:41:53 +00:00
|
|
|
const sinon = require('sinon')
|
2019-04-23 11:50:08 +00:00
|
|
|
|
|
|
|
function makeMockSocket (headers, query) {
|
2019-05-03 11:40:35 +00:00
|
|
|
const broadCastChannelCache = {}
|
2019-04-23 11:50:08 +00:00
|
|
|
return {
|
2019-05-24 04:41:53 +00:00
|
|
|
id: Math.round(Math.random() * 10000),
|
2019-04-23 11:50:08 +00:00
|
|
|
handshake: {
|
|
|
|
headers: Object.assign({}, headers),
|
|
|
|
query: Object.assign({}, query)
|
2019-05-24 04:41:53 +00:00
|
|
|
},
|
2019-05-03 11:40:35 +00:00
|
|
|
on: sinon.fake(),
|
2019-05-03 12:02:52 +00:00
|
|
|
emit: sinon.fake(),
|
2019-05-03 11:40:35 +00:00
|
|
|
broadCastChannelCache: {},
|
|
|
|
broadcast: {
|
|
|
|
to: (channel) => {
|
|
|
|
if (!broadCastChannelCache[channel]) {
|
|
|
|
broadCastChannelCache[channel] = {
|
|
|
|
channel: channel,
|
|
|
|
emit: sinon.fake()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return broadCastChannelCache[channel]
|
|
|
|
}
|
|
|
|
}
|
2019-04-23 11:50:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-24 04:41:53 +00:00
|
|
|
function removeModuleFromRequireCache (modulePath) {
|
|
|
|
delete require.cache[require.resolve(modulePath)]
|
|
|
|
}
|
|
|
|
|
2019-04-23 11:50:08 +00:00
|
|
|
describe('realtime', function () {
|
|
|
|
describe('extractNoteIdFromSocket', function () {
|
|
|
|
beforeEach(() => {
|
|
|
|
mock('../lib/logger', {})
|
|
|
|
mock('../lib/history', {})
|
|
|
|
mock('../lib/models', {})
|
|
|
|
})
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
delete require.cache[require.resolve('../lib/realtime')]
|
|
|
|
mock.stopAll()
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('urlPath not set', function () {
|
|
|
|
beforeEach(() => {
|
|
|
|
mock('../lib/config', {})
|
|
|
|
realtime = require('../lib/realtime')
|
|
|
|
})
|
|
|
|
|
|
|
|
let realtime
|
|
|
|
|
|
|
|
it('return false if socket or socket.handshake not exists', function () {
|
|
|
|
let noteId = realtime.extractNoteIdFromSocket()
|
|
|
|
assert.strictEqual(false, noteId)
|
|
|
|
|
|
|
|
noteId = realtime.extractNoteIdFromSocket({})
|
|
|
|
assert.strictEqual(false, noteId)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('return false if query not set and referer not set', function () {
|
|
|
|
let noteId = realtime.extractNoteIdFromSocket(makeMockSocket({
|
|
|
|
otherHeader: 1
|
|
|
|
}, {
|
|
|
|
otherQuery: 1
|
|
|
|
}))
|
|
|
|
assert.strictEqual(false, noteId)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('return noteId from query', function () {
|
|
|
|
// Arrange
|
|
|
|
const incomingNoteId = 'myNoteId'
|
|
|
|
const incomingSocket = makeMockSocket(undefined, { noteId: incomingNoteId })
|
|
|
|
|
|
|
|
// Act
|
|
|
|
const noteId = realtime.extractNoteIdFromSocket(incomingSocket)
|
|
|
|
// Assert
|
|
|
|
assert.strictEqual(noteId, incomingNoteId)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('return noteId from old method (referer)', function () {
|
|
|
|
// Arrange
|
|
|
|
const incomingNoteId = 'myNoteId'
|
|
|
|
const incomingSocket = makeMockSocket({
|
|
|
|
referer: `https://localhost:3000/${incomingNoteId}`
|
|
|
|
})
|
|
|
|
|
|
|
|
// Act
|
|
|
|
const noteId = realtime.extractNoteIdFromSocket(incomingSocket)
|
|
|
|
// Assert
|
|
|
|
assert.strictEqual(noteId, incomingNoteId)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('urlPath is set', function () {
|
|
|
|
let realtime
|
|
|
|
it('return noteId from old method (referer) and urlPath set', function () {
|
|
|
|
// Arrange
|
|
|
|
const urlPath = 'hello'
|
|
|
|
mock('../lib/config', {
|
|
|
|
urlPath: urlPath
|
|
|
|
})
|
|
|
|
realtime = require('../lib/realtime')
|
|
|
|
const incomingNoteId = 'myNoteId'
|
|
|
|
const incomingSocket = makeMockSocket({
|
|
|
|
referer: `https://localhost:3000/${urlPath}/${incomingNoteId}`
|
|
|
|
})
|
|
|
|
|
|
|
|
// Act
|
|
|
|
const noteId = realtime.extractNoteIdFromSocket(incomingSocket)
|
|
|
|
// Assert
|
|
|
|
assert.strictEqual(noteId, incomingNoteId)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
2019-05-24 04:41:53 +00:00
|
|
|
|
|
|
|
describe('parseNoteIdFromSocket', function () {
|
|
|
|
let realtime
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
mock('../lib/logger', {})
|
|
|
|
mock('../lib/history', {})
|
|
|
|
mock('../lib/models', {
|
|
|
|
Note: {
|
|
|
|
parseNoteId: function (noteId, callback) {
|
|
|
|
callback(null, noteId)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
mock('../lib/config', {})
|
|
|
|
})
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
removeModuleFromRequireCache('../lib/realtime')
|
|
|
|
mock.stopAll()
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should return null when socket not send noteId', function () {
|
|
|
|
realtime = require('../lib/realtime')
|
|
|
|
const mockSocket = makeMockSocket()
|
|
|
|
const fakeCallback = sinon.fake()
|
|
|
|
realtime.parseNoteIdFromSocket(mockSocket, fakeCallback)
|
|
|
|
assert(fakeCallback.called)
|
|
|
|
assert.deepStrictEqual(fakeCallback.getCall(0).args, [null, null])
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('noteId exists', function () {
|
|
|
|
beforeEach(() => {
|
|
|
|
mock('../lib/models', {
|
|
|
|
Note: {
|
|
|
|
parseNoteId: function (noteId, callback) {
|
|
|
|
callback(null, noteId)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
it('should return noteId when noteId exists', function () {
|
|
|
|
realtime = require('../lib/realtime')
|
|
|
|
const noteId = '123456'
|
|
|
|
const mockSocket = makeMockSocket(undefined, {
|
|
|
|
noteId: noteId
|
|
|
|
})
|
|
|
|
realtime = require('../lib/realtime')
|
|
|
|
const fakeCallback = sinon.fake()
|
|
|
|
realtime.parseNoteIdFromSocket(mockSocket, fakeCallback)
|
|
|
|
assert(fakeCallback.called)
|
|
|
|
assert.deepStrictEqual(fakeCallback.getCall(0).args, [null, noteId])
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('noteId not exists', function () {
|
|
|
|
beforeEach(() => {
|
|
|
|
mock('../lib/models', {
|
|
|
|
Note: {
|
|
|
|
parseNoteId: function (noteId, callback) {
|
|
|
|
callback(null, null)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
it('should return null when noteId not exists', function () {
|
|
|
|
realtime = require('../lib/realtime')
|
|
|
|
const noteId = '123456'
|
|
|
|
const mockSocket = makeMockSocket(undefined, {
|
|
|
|
noteId: noteId
|
|
|
|
})
|
|
|
|
realtime = require('../lib/realtime')
|
|
|
|
const fakeCallback = sinon.fake()
|
|
|
|
realtime.parseNoteIdFromSocket(mockSocket, fakeCallback)
|
|
|
|
assert(fakeCallback.called)
|
|
|
|
assert.deepStrictEqual(fakeCallback.getCall(0).args, [null, null])
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('parse note error', function () {
|
|
|
|
beforeEach(() => {
|
|
|
|
mock('../lib/models', {
|
|
|
|
Note: {
|
|
|
|
parseNoteId: function (noteId, callback) {
|
|
|
|
/* eslint-disable-next-line */
|
|
|
|
callback('error', null)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
it('should return error when noteId parse error', function () {
|
|
|
|
realtime = require('../lib/realtime')
|
|
|
|
const noteId = '123456'
|
|
|
|
const mockSocket = makeMockSocket(undefined, {
|
|
|
|
noteId: noteId
|
|
|
|
})
|
|
|
|
realtime = require('../lib/realtime')
|
|
|
|
const fakeCallback = sinon.fake()
|
|
|
|
realtime.parseNoteIdFromSocket(mockSocket, fakeCallback)
|
|
|
|
assert(fakeCallback.called)
|
|
|
|
assert.deepStrictEqual(fakeCallback.getCall(0).args, ['error', null])
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('update note is dirty timer', function () {
|
|
|
|
let realtime
|
|
|
|
beforeEach(() => {
|
|
|
|
mock('../lib/logger', {
|
|
|
|
error: () => {
|
|
|
|
}
|
|
|
|
})
|
|
|
|
mock('../lib/history', {})
|
|
|
|
mock('../lib/models', {
|
|
|
|
Revision: {
|
|
|
|
saveAllNotesRevision: () => {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
mock('../lib/config', {})
|
|
|
|
})
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
removeModuleFromRequireCache('../lib/realtime')
|
|
|
|
mock.stopAll()
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should update note when note is dirty', (done) => {
|
|
|
|
const clock = sinon.useFakeTimers()
|
|
|
|
realtime = require('../lib/realtime')
|
|
|
|
sinon.stub(realtime, 'updateNote').callsFake(function (note, callback) {
|
|
|
|
callback(null, null)
|
|
|
|
})
|
|
|
|
const socketIoEmitFake = sinon.fake()
|
|
|
|
realtime.io = {
|
|
|
|
to: sinon.stub().callsFake(function () {
|
|
|
|
return {
|
|
|
|
emit: socketIoEmitFake
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
realtime.notes['note1'] = {
|
|
|
|
server: {
|
|
|
|
isDirty: false
|
|
|
|
},
|
|
|
|
socks: []
|
|
|
|
}
|
|
|
|
let note2 = {
|
|
|
|
server: {
|
|
|
|
isDirty: true
|
|
|
|
},
|
|
|
|
socks: []
|
|
|
|
}
|
|
|
|
realtime.notes['note2'] = note2
|
|
|
|
|
|
|
|
clock.tick(1000)
|
|
|
|
clock.restore()
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
assert(note2.server.isDirty === false)
|
|
|
|
done()
|
|
|
|
}, 50)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('updateNote', function () {
|
|
|
|
let realtime, fakeNote
|
|
|
|
beforeEach(() => {
|
|
|
|
mock('../lib/logger', {
|
|
|
|
error: () => {
|
|
|
|
}
|
|
|
|
})
|
|
|
|
mock('../lib/history', {})
|
|
|
|
mock('../lib/models', {
|
|
|
|
Note: {
|
|
|
|
findOne: async function () {
|
|
|
|
return fakeNote
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
mock('../lib/config', {})
|
|
|
|
})
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
mock.stopAll()
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should return null when note not found', function (done) {
|
|
|
|
fakeNote = null
|
|
|
|
realtime = require('../lib/realtime')
|
|
|
|
|
|
|
|
sinon.stub(realtime, 'finishUpdateNote').callsFake(function (a, b, callback) {
|
|
|
|
callback(null, b)
|
|
|
|
})
|
|
|
|
|
|
|
|
const fakeCallback = sinon.fake()
|
|
|
|
realtime.updateNote({ id: '123' }, fakeCallback)
|
|
|
|
setTimeout(() => {
|
|
|
|
assert.ok(fakeCallback.called)
|
|
|
|
assert.deepStrictEqual(fakeCallback.getCall(0).args, [null, null])
|
|
|
|
sinon.restore()
|
|
|
|
done()
|
|
|
|
}, 50)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('finishUpdateNote', function () {
|
|
|
|
let realtime
|
|
|
|
beforeEach(() => {
|
|
|
|
mock('../lib/logger', {})
|
|
|
|
mock('../lib/history', {})
|
|
|
|
mock('../lib/models', {
|
|
|
|
Note: {
|
|
|
|
parseNoteTitle: (data) => (data)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
mock('../lib/config', {})
|
|
|
|
realtime = require('../lib/realtime')
|
|
|
|
})
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
removeModuleFromRequireCache('../lib/realtime')
|
|
|
|
mock.stopAll()
|
|
|
|
})
|
|
|
|
|
|
|
|
it('return null when note is null', () => {
|
|
|
|
const fakeCallback = sinon.fake()
|
|
|
|
|
|
|
|
realtime.finishUpdateNote(null, {}, fakeCallback)
|
|
|
|
|
|
|
|
assert.ok(fakeCallback.calledOnce)
|
|
|
|
assert.deepStrictEqual(fakeCallback.lastCall.args, [null, null])
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('connection', function () {
|
|
|
|
let realtime
|
|
|
|
beforeEach(() => {
|
|
|
|
mock('../lib/logger', {
|
|
|
|
error: () => {
|
|
|
|
}
|
|
|
|
})
|
|
|
|
mock('../lib/history', {})
|
|
|
|
mock('../lib/models', {
|
|
|
|
Note: {
|
|
|
|
parseNoteTitle: (data) => (data)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
mock('../lib/config', {})
|
|
|
|
realtime = require('../lib/realtime')
|
|
|
|
})
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
removeModuleFromRequireCache('../lib/realtime')
|
|
|
|
mock.stopAll()
|
|
|
|
sinon.restore()
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('fail', function () {
|
|
|
|
it('should fast return when server not start', () => {
|
|
|
|
const mockSocket = makeMockSocket()
|
|
|
|
realtime.maintenance = true
|
|
|
|
const spy = sinon.spy(realtime, 'parseNoteIdFromSocket')
|
|
|
|
realtime.connection(mockSocket)
|
|
|
|
assert(!spy.called)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should failed when parse noteId occur error', () => {
|
|
|
|
const mockSocket = makeMockSocket()
|
|
|
|
realtime.maintenance = false
|
|
|
|
const parseNoteIdFromSocketSpy = sinon.stub(realtime, 'parseNoteIdFromSocket').callsFake((socket, callback) => {
|
|
|
|
/* eslint-disable-next-line */
|
|
|
|
callback('error', null)
|
|
|
|
})
|
|
|
|
|
|
|
|
const failConnectionSpy = sinon.stub(realtime, 'failConnection')
|
|
|
|
|
|
|
|
realtime.connection(mockSocket)
|
|
|
|
|
|
|
|
assert(parseNoteIdFromSocketSpy.called)
|
|
|
|
assert(failConnectionSpy.calledOnce)
|
|
|
|
assert.deepStrictEqual(failConnectionSpy.lastCall.args, [500, 'error', mockSocket])
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should failed when noteId not exists', () => {
|
|
|
|
const mockSocket = makeMockSocket()
|
|
|
|
realtime.maintenance = false
|
|
|
|
const parseNoteIdFromSocketSpy = sinon.stub(realtime, 'parseNoteIdFromSocket').callsFake((socket, callback) => {
|
|
|
|
/* eslint-disable-next-line */
|
|
|
|
callback(null, null)
|
|
|
|
})
|
|
|
|
|
|
|
|
const failConnectionSpy = sinon.stub(realtime, 'failConnection')
|
|
|
|
|
|
|
|
realtime.connection(mockSocket)
|
|
|
|
|
|
|
|
assert(parseNoteIdFromSocketSpy.called)
|
|
|
|
assert(failConnectionSpy.calledOnce)
|
|
|
|
assert.deepStrictEqual(failConnectionSpy.lastCall.args, [404, 'note id not found', mockSocket])
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should success connect', function () {
|
|
|
|
const mockSocket = makeMockSocket()
|
|
|
|
const noteId = 'note123'
|
|
|
|
realtime.maintenance = false
|
|
|
|
const parseNoteIdFromSocketSpy = sinon.stub(realtime, 'parseNoteIdFromSocket').callsFake((socket, callback) => {
|
|
|
|
/* eslint-disable-next-line */
|
|
|
|
callback(null, noteId)
|
|
|
|
})
|
|
|
|
const failConnectionStub = sinon.stub(realtime, 'failConnection')
|
|
|
|
const updateUserDataStub = sinon.stub(realtime, 'updateUserData')
|
|
|
|
const startConnectionStub = sinon.stub(realtime, 'startConnection')
|
|
|
|
|
|
|
|
realtime.connection(mockSocket)
|
|
|
|
|
|
|
|
assert.ok(parseNoteIdFromSocketSpy.calledOnce)
|
|
|
|
|
|
|
|
assert(failConnectionStub.called === false)
|
|
|
|
assert(updateUserDataStub.calledOnce)
|
|
|
|
assert(startConnectionStub.calledOnce)
|
|
|
|
assert(mockSocket.on.callCount === 11)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('socket event', function () {
|
|
|
|
let realtime
|
2019-05-03 14:20:30 +00:00
|
|
|
const noteId = 'note123'
|
2019-05-24 04:41:53 +00:00
|
|
|
let clientSocket
|
|
|
|
const eventFuncMap = new Map()
|
|
|
|
beforeEach(() => {
|
|
|
|
mock('../lib/logger', {
|
|
|
|
error: () => {
|
2019-05-24 10:05:16 +00:00
|
|
|
},
|
|
|
|
info: () => {}
|
2019-05-24 04:41:53 +00:00
|
|
|
})
|
|
|
|
mock('../lib/history', {})
|
|
|
|
mock('../lib/models', {
|
|
|
|
Note: {
|
|
|
|
parseNoteTitle: (data) => (data)
|
|
|
|
}
|
|
|
|
})
|
2019-05-03 12:02:52 +00:00
|
|
|
mock('../lib/config', {
|
|
|
|
fullversion: '1.5.0',
|
|
|
|
minimumCompatibleVersion: '1.0.0'
|
|
|
|
})
|
2019-05-24 04:41:53 +00:00
|
|
|
realtime = require('../lib/realtime')
|
|
|
|
|
|
|
|
// get all socket event handler
|
|
|
|
clientSocket = makeMockSocket()
|
|
|
|
clientSocket.on = function (event, func) {
|
|
|
|
eventFuncMap.set(event, func)
|
|
|
|
}
|
|
|
|
realtime.maintenance = false
|
|
|
|
sinon.stub(realtime, 'parseNoteIdFromSocket').callsFake((socket, callback) => {
|
|
|
|
/* eslint-disable-next-line */
|
|
|
|
callback(null, noteId)
|
|
|
|
})
|
2019-05-24 10:05:16 +00:00
|
|
|
const wrappedFuncs = []
|
|
|
|
wrappedFuncs.push(sinon.stub(realtime, 'failConnection'))
|
|
|
|
wrappedFuncs.push(sinon.stub(realtime, 'updateUserData'))
|
|
|
|
wrappedFuncs.push(sinon.stub(realtime, 'startConnection'))
|
2019-05-24 04:41:53 +00:00
|
|
|
realtime.connection(clientSocket)
|
2019-05-24 10:05:16 +00:00
|
|
|
|
|
|
|
wrappedFuncs.forEach((wrappedFunc) => {
|
|
|
|
wrappedFunc.restore()
|
|
|
|
})
|
|
|
|
|
2019-05-24 04:41:53 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
removeModuleFromRequireCache('../lib/realtime')
|
|
|
|
mock.stopAll()
|
|
|
|
sinon.restore()
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('refresh', function () {
|
|
|
|
it('should call refresh', () => {
|
|
|
|
const refreshFunc = eventFuncMap.get('refresh')
|
|
|
|
const emitRefreshStub = sinon.stub(realtime, 'emitRefresh')
|
|
|
|
refreshFunc()
|
|
|
|
assert(emitRefreshStub.calledOnce)
|
|
|
|
assert.deepStrictEqual(emitRefreshStub.lastCall.args[0], clientSocket)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('user status', function () {
|
|
|
|
it('should call emitUserStatus and update user data', () => {
|
|
|
|
const userStatusFunc = eventFuncMap.get('user status')
|
|
|
|
const emitUserStatusStub = sinon.stub(realtime, 'emitUserStatus')
|
|
|
|
realtime.notes[noteId] = {}
|
|
|
|
|
|
|
|
const userData = {
|
|
|
|
idle: true,
|
|
|
|
type: 'xs'
|
|
|
|
}
|
|
|
|
userStatusFunc(userData)
|
|
|
|
assert(emitUserStatusStub.calledOnce)
|
|
|
|
assert.deepStrictEqual(emitUserStatusStub.lastCall.args[0], clientSocket)
|
|
|
|
assert(realtime.users[clientSocket.id].idle === true)
|
|
|
|
assert(realtime.users[clientSocket.id].type === 'xs')
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should call emitUserStatus without userdata', () => {
|
|
|
|
const userStatusFunc = eventFuncMap.get('user status')
|
|
|
|
const emitUserStatusStub = sinon.stub(realtime, 'emitUserStatus')
|
|
|
|
realtime.notes[noteId] = {}
|
|
|
|
userStatusFunc()
|
|
|
|
assert(emitUserStatusStub.calledOnce)
|
|
|
|
assert.deepStrictEqual(emitUserStatusStub.lastCall.args[0], clientSocket)
|
|
|
|
assert(realtime.users[clientSocket.id].idle === false)
|
|
|
|
assert(realtime.users[clientSocket.id].type === null)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should not call emitUserStatus when user not exists', () => {
|
|
|
|
const userStatusFunc = eventFuncMap.get('user status')
|
|
|
|
const emitUserStatusStub = sinon.stub(realtime, 'emitUserStatus')
|
|
|
|
realtime.notes[noteId] = {}
|
|
|
|
delete realtime.users[clientSocket.id]
|
|
|
|
const userData = {
|
|
|
|
idle: true,
|
|
|
|
type: 'xs'
|
|
|
|
}
|
|
|
|
userStatusFunc(userData)
|
|
|
|
assert(emitUserStatusStub.called === false)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should not call emitUserStatus when note not exists', () => {
|
|
|
|
const userStatusFunc = eventFuncMap.get('user status')
|
|
|
|
const emitUserStatusStub = sinon.stub(realtime, 'emitUserStatus')
|
|
|
|
realtime.notes = {}
|
|
|
|
realtime.users[clientSocket.id] = {}
|
|
|
|
const userData = {
|
|
|
|
idle: true,
|
|
|
|
type: 'xs'
|
|
|
|
}
|
|
|
|
userStatusFunc(userData)
|
|
|
|
assert(emitUserStatusStub.called === false)
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
|
2019-05-03 11:08:33 +00:00
|
|
|
describe('disconnect', function () {
|
|
|
|
it('should push socket to disconnect queue and call disconnect function', () => {
|
|
|
|
const disconnectFunc = eventFuncMap.get('disconnect')
|
|
|
|
const disconnectStub = sinon.stub(realtime, 'disconnect')
|
|
|
|
disconnectFunc()
|
|
|
|
assert(realtime.disconnectSocketQueue.length === 1)
|
|
|
|
assert(disconnectStub.calledOnce)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should quick return when socket is in disconnect queue', () => {
|
|
|
|
const disconnectFunc = eventFuncMap.get('disconnect')
|
|
|
|
const disconnectStub = sinon.stub(realtime, 'disconnect')
|
|
|
|
realtime.disconnectSocketQueue.push(clientSocket)
|
|
|
|
disconnectFunc()
|
|
|
|
assert(disconnectStub.called === false)
|
|
|
|
})
|
2019-05-03 11:40:35 +00:00
|
|
|
})
|
|
|
|
|
2019-05-03 14:20:30 +00:00
|
|
|
;['cursor focus', 'cursor activity', 'cursor blur'].forEach((event) => {
|
2019-05-03 11:40:35 +00:00
|
|
|
describe(event, function () {
|
|
|
|
let cursorFocusFunc
|
|
|
|
|
|
|
|
const cursorData = {
|
|
|
|
cursor: 10
|
|
|
|
}
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
cursorFocusFunc = eventFuncMap.get(event)
|
|
|
|
realtime.notes[noteId] = {}
|
|
|
|
})
|
2019-05-03 11:08:33 +00:00
|
|
|
|
2019-05-03 11:40:35 +00:00
|
|
|
it('should broadcast to all client', () => {
|
|
|
|
cursorFocusFunc(cursorData)
|
|
|
|
const broadChannelEmitFake = clientSocket.broadcast.to(noteId).emit
|
|
|
|
assert(broadChannelEmitFake.calledOnce)
|
|
|
|
assert(broadChannelEmitFake.lastCall.args[0] === event)
|
|
|
|
if (event === 'cursor blur') {
|
|
|
|
assert(broadChannelEmitFake.lastCall.args[1].id === clientSocket.id)
|
|
|
|
} else {
|
|
|
|
assert.deepStrictEqual(broadChannelEmitFake.lastCall.args[1].cursor, cursorData)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should not broadcast when note not exists', () => {
|
|
|
|
delete realtime.notes[noteId]
|
|
|
|
cursorFocusFunc(cursorData)
|
|
|
|
const broadChannelEmitFake = clientSocket.broadcast.to(noteId).emit
|
|
|
|
assert(broadChannelEmitFake.called === false)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should not broadcast when user not exists', () => {
|
|
|
|
delete realtime.users[clientSocket.id]
|
|
|
|
cursorFocusFunc(cursorData)
|
|
|
|
const broadChannelEmitFake = clientSocket.broadcast.to(noteId).emit
|
|
|
|
assert(broadChannelEmitFake.called === false)
|
|
|
|
})
|
|
|
|
})
|
2019-05-03 11:08:33 +00:00
|
|
|
})
|
|
|
|
|
2019-05-03 12:02:52 +00:00
|
|
|
describe('version', function () {
|
|
|
|
it('should emit server version ', () => {
|
|
|
|
const versionFunc = eventFuncMap.get('version')
|
|
|
|
versionFunc()
|
|
|
|
assert(clientSocket.emit.called)
|
|
|
|
assert(clientSocket.emit.lastCall.args[0], 'version')
|
|
|
|
assert.deepStrictEqual(clientSocket.emit.lastCall.args[1], {
|
|
|
|
version: '1.5.0',
|
|
|
|
minimumCompatibleVersion: '1.0.0'
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2019-05-03 14:20:30 +00:00
|
|
|
describe('online users', function () {
|
|
|
|
it('should return online user list', () => {
|
|
|
|
const onlineUsersFunc = eventFuncMap.get('online users')
|
|
|
|
realtime.notes[noteId] = {
|
|
|
|
users: {
|
|
|
|
10: {
|
|
|
|
id: 10
|
|
|
|
},
|
|
|
|
20: {
|
|
|
|
id: 20
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
onlineUsersFunc()
|
|
|
|
assert(clientSocket.emit.called)
|
|
|
|
assert(clientSocket.emit.lastCall.args[0] === 'online users')
|
|
|
|
let returnUserList = clientSocket.emit.lastCall.args[1].users
|
|
|
|
assert(returnUserList.length === 2)
|
|
|
|
assert(returnUserList[0].id === 10)
|
|
|
|
assert(returnUserList[1].id === 20)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should not return user list when note not exists', () => {
|
|
|
|
const onlineUsersFunc = eventFuncMap.get('online users')
|
|
|
|
onlineUsersFunc()
|
|
|
|
assert(clientSocket.emit.called === false)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2019-05-24 10:05:16 +00:00
|
|
|
describe('user changed', function () {
|
|
|
|
it('should call updateUserData', () => {
|
|
|
|
const userChangedFunc = eventFuncMap.get('user changed')
|
|
|
|
realtime.notes[noteId] = {
|
|
|
|
users: {
|
|
|
|
[clientSocket.id]: {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const updateUserDataStub = sinon.stub(realtime, 'updateUserData')
|
|
|
|
const emitOnlineUsersStub = sinon.stub(realtime, 'emitOnlineUsers')
|
|
|
|
userChangedFunc()
|
|
|
|
assert(updateUserDataStub.calledOnce)
|
|
|
|
assert(emitOnlineUsersStub.calledOnce)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should direct return when note not exists', () => {
|
|
|
|
const userChangedFunc = eventFuncMap.get('user changed')
|
|
|
|
const updateUserDataStub = sinon.stub(realtime, 'updateUserData')
|
|
|
|
const emitOnlineUsersStub = sinon.stub(realtime, 'emitOnlineUsers')
|
|
|
|
userChangedFunc()
|
|
|
|
assert(updateUserDataStub.called === false)
|
|
|
|
assert(emitOnlineUsersStub.called === false)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should direct return when note not exists', () => {
|
|
|
|
const userChangedFunc = eventFuncMap.get('user changed')
|
|
|
|
realtime.notes[noteId] = {
|
|
|
|
users: {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delete realtime.users[clientSocket.id]
|
|
|
|
const updateUserDataStub = sinon.stub(realtime, 'updateUserData')
|
|
|
|
const emitOnlineUsersStub = sinon.stub(realtime, 'emitOnlineUsers')
|
|
|
|
userChangedFunc()
|
|
|
|
assert(updateUserDataStub.called === false)
|
|
|
|
assert(emitOnlineUsersStub.called === false)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2019-05-03 18:43:04 +00:00
|
|
|
describe('permission', function () {
|
|
|
|
|
|
|
|
})
|
|
|
|
|
2019-05-24 04:41:53 +00:00
|
|
|
})
|
2019-04-23 11:50:08 +00:00
|
|
|
})
|