teletype-client-modified
Version:
I have modified the library developed by Atom to make it compatible with non-browser facility. I am using it only for experimentation purposes. I will remove this package post my experiment and raise a PR at Atom to finally merge this package so that this
272 lines (227 loc) • 12.1 kB
JavaScript
const assert = require('assert')
const {Disposable} = require('event-kit')
const {startTestServer} = require('@atom/teletype-server')
const {buildPeerPool, clearPeerPools} = require('./helpers/peer-pools')
const condition = require('./helpers/condition')
const setEqual = require('./helpers/set-equal')
const FakePortalDelegate = require('./helpers/fake-portal-delegate')
const Portal = require('../lib/portal')
suite('Portal', () => {
let server
suiteSetup(async () => {
server = await startTestServer()
})
suiteTeardown(() => {
return server.stop()
})
setup(() => {
return server.reset()
})
teardown(() => {
clearPeerPools()
})
suite('join', () => {
test('throws and disposes itself when a network error occurs', async () => {
const peerPool = await buildPeerPool('guest', server)
const portal = new Portal({id: 'id', hostPeerId: 'host', siteId: 2, peerPool})
portal.network.connectTo = function () {
throw new Error('an error')
}
let error
try {
await portal.join()
} catch (e) {
error = e
}
assert.equal(error.message, 'an error')
assert(portal.disposed)
})
})
test('joining and leaving a portal', async () => {
const hostPeerPool = await buildPeerPool('host', server)
const guest1PeerPool = await buildPeerPool('guest1', server)
const guest2PeerPool = await buildPeerPool('guest2', server)
const hostPortal = await buildPortal('portal', hostPeerPool)
const guest1Portal = await buildPortal('portal', guest1PeerPool, 'host')
const guest2Portal = await buildPortal('portal', guest2PeerPool, 'host')
await guest1Portal.join()
await guest2Portal.join()
assert(setEqual(hostPortal.getActiveSiteIds(), [1, 2, 3]))
assert(setEqual(guest1Portal.getActiveSiteIds(), [1, 2, 3]))
assert(setEqual(guest2Portal.getActiveSiteIds(), [1, 2, 3]))
assert.deepEqual(hostPortal.testDelegate.joinEvents, [2, 3])
assert.deepEqual(guest1Portal.testDelegate.joinEvents, [3])
assert.deepEqual(guest2Portal.testDelegate.joinEvents, [])
guest1Portal.dispose()
await condition(() => (
setEqual(hostPortal.getActiveSiteIds(), [1, 3]) &&
setEqual(guest1Portal.getActiveSiteIds(), [2]) &&
setEqual(guest2Portal.getActiveSiteIds(), [1, 3])
))
assert.deepEqual(hostPortal.testDelegate.leaveEvents, [2])
assert.deepEqual(guest1Portal.testDelegate.leaveEvents, [])
assert.deepEqual(guest2Portal.testDelegate.leaveEvents, [2])
// Ensure leave event is not emitted when the host disconnects.
hostPortal.dispose()
await condition(() => (
setEqual(hostPortal.getActiveSiteIds(), [1]) &&
setEqual(guest1Portal.getActiveSiteIds(), [2]) &&
setEqual(guest2Portal.getActiveSiteIds(), [3])
))
assert.deepEqual(hostPortal.testDelegate.leaveEvents, [2])
assert.deepEqual(guest1Portal.testDelegate.leaveEvents, [])
assert.deepEqual(guest2Portal.testDelegate.leaveEvents, [2])
assert(guest2Portal.testDelegate.hasHostClosedPortal())
})
test('site identities', async () => {
const hostIdentity = {login: 'host'}
const guest1Identity = {login: 'guest1'}
const guest2Identity = {login: 'guest2'}
server.identityProvider.setIdentitiesByToken({
'host-token': hostIdentity,
'guest1-token': guest1Identity,
'guest2-token': guest2Identity
})
const hostPeerPool = await buildPeerPool('host', server)
const guest1PeerPool = await buildPeerPool('guest1', server)
const guest2PeerPool = await buildPeerPool('guest2', server)
const hostPortal = await buildPortal('portal', hostPeerPool)
const guest1Portal = await buildPortal('portal', guest1PeerPool, 'host')
const guest2Portal = await buildPortal('portal', guest2PeerPool, 'host')
await guest1Portal.join()
await guest2Portal.join()
assert.equal(hostPortal.getSiteIdentity(1).login, hostIdentity.login)
assert.equal(hostPortal.getSiteIdentity(2).login, guest1Identity.login)
assert.equal(hostPortal.getSiteIdentity(3).login, guest2Identity.login)
assert.equal(guest1Portal.getSiteIdentity(1).login, hostIdentity.login)
assert.equal(guest1Portal.getSiteIdentity(2).login, guest1Identity.login)
assert.equal(guest1Portal.getSiteIdentity(3).login, guest2Identity.login)
assert.equal(guest2Portal.getSiteIdentity(1).login, hostIdentity.login)
assert.equal(guest2Portal.getSiteIdentity(2).login, guest1Identity.login)
assert.equal(guest2Portal.getSiteIdentity(3).login, guest2Identity.login)
})
test('notifying delegate of active position of other sites', async () => {
const hostPeerPool = await buildPeerPool('host', server)
const guestPeerPool = await buildPeerPool('guest', server)
const hostPortal = await buildPortal('portal', hostPeerPool)
const guestPortal = await buildPortal('portal', guestPeerPool, 'host')
const joinPromise = guestPortal.join()
await guestPortal.networkConnectionPromise
const bufferProxy = await hostPortal.createBufferProxy({uri: 'uri', text: ''})
const editorProxy = await hostPortal.createEditorProxy({bufferProxy})
hostPortal.activateEditorProxy(editorProxy)
// Guest has only partially joined, so it's not yet an active site.
let activeSiteIds = Object.keys(hostPortal.testDelegate.activePositionsBySiteId)
assert.deepEqual(activeSiteIds, ['1'])
// When guest finally joins, it becomes an active site.
await joinPromise
activeSiteIds = Object.keys(hostPortal.testDelegate.activePositionsBySiteId)
assert.deepEqual(activeSiteIds, ['1', '2'])
})
suite('changing active editor proxy', () => {
test('host only notifies guests when active editor proxy has changed', async () => {
const hostPeerPool = await buildPeerPool('host', server)
const guestPeerPool = await buildPeerPool('guest', server)
const hostPortal = await buildPortal('portal', hostPeerPool)
const guestPortal = await buildPortal('portal', guestPeerPool, 'host')
await guestPortal.join()
guestPortal.testDelegate.tetherEditorProxyChangeCount = 0
// Don't notify guests when setting the active editor proxy to the same value it currently has.
hostPortal.activateEditorProxy(null)
// Set the active editor proxy to a different value to ensure guests are notified only of this change.
const originalBufferProxy = await hostPortal.createBufferProxy({uri: 'original-uri', text: ''})
const originalEditorProxy = await hostPortal.createEditorProxy({bufferProxy: originalBufferProxy})
hostPortal.activateEditorProxy(originalEditorProxy)
await condition(() => (
guestPortal.testDelegate.getTetherBufferProxyURI() === 'original-uri' &&
guestPortal.testDelegate.tetherEditorProxyChangeCount === 1
))
})
test('guest gracefully handles race conditions', async () => {
const hostPeerPool = await buildPeerPool('host', server)
const guestPeerPool = await buildPeerPool('guest', server)
const hostPortal = await buildPortal('portal', hostPeerPool)
const guestPortal = await buildPortal('portal', guestPeerPool, 'host')
await guestPortal.join()
guestPortal.testDelegate.tetherEditorProxyChangeCount = 0
// Ensure no race condition occurs on the guest when fetching new editor
// proxies for the first time and, at the same time, receiving a request
// to switch to a previous value of active editor proxy.
const newBufferProxy = await hostPortal.createBufferProxy({uri: 'new-uri', text: ''})
const newEditorProxy = await hostPortal.createEditorProxy({bufferProxy: newBufferProxy})
hostPortal.activateEditorProxy(newEditorProxy)
hostPortal.activateEditorProxy(null)
await condition(() => (
guestPortal.testDelegate.getTetherBufferProxyURI() === null &&
guestPortal.testDelegate.tetherEditorProxyChangeCount === 2
))
})
test('guest gracefully handles switching to an editor proxy whose buffer has already been destroyed', async () => {
const hostPeerPool = await buildPeerPool('host', server)
const guestPeerPool = await buildPeerPool('guest', server)
const hostPortal = await buildPortal('portal', hostPeerPool)
const guestPortal = await buildPortal('portal', guestPeerPool, 'host')
await guestPortal.join()
guestPortal.testDelegate.tetherEditorProxyChangeCount = 0
const bufferProxy1 = await hostPortal.createBufferProxy({uri: 'uri-1', text: ''})
const editorProxy1 = await hostPortal.createEditorProxy({bufferProxy: bufferProxy1})
const bufferProxy2 = await hostPortal.createBufferProxy({uri: 'uri-2', text: ''})
const editorProxy2 = await hostPortal.createEditorProxy({bufferProxy: bufferProxy2})
hostPortal.activateEditorProxy(editorProxy1)
bufferProxy1.dispose()
hostPortal.activateEditorProxy(null)
hostPortal.activateEditorProxy(editorProxy2)
await condition(() => (
guestPortal.testDelegate.getTetherBufferProxyURI() === 'uri-2' &&
guestPortal.testDelegate.tetherEditorProxyChangeCount === 1
))
})
test('guest gracefully handles switching to an editor proxy that has already been destroyed', async () => {
const hostPeerPool = await buildPeerPool('host', server)
const guestPeerPool = await buildPeerPool('guest', server)
const hostPortal = await buildPortal('portal', hostPeerPool)
const guestPortal = await buildPortal('portal', guestPeerPool, 'host')
await guestPortal.join()
guestPortal.testDelegate.tetherEditorProxyChangeCount = 0
const bufferProxy1 = await hostPortal.createBufferProxy({uri: 'uri-1', text: ''})
const editorProxy1 = await hostPortal.createEditorProxy({bufferProxy: bufferProxy1})
const bufferProxy2 = await hostPortal.createBufferProxy({uri: 'uri-2', text: ''})
const editorProxy2 = await hostPortal.createEditorProxy({bufferProxy: bufferProxy2})
hostPortal.activateEditorProxy(editorProxy1)
editorProxy1.dispose()
hostPortal.activateEditorProxy(null)
hostPortal.activateEditorProxy(editorProxy2)
await condition(() => (
guestPortal.testDelegate.getTetherBufferProxyURI() === 'uri-2' &&
guestPortal.testDelegate.tetherEditorProxyChangeCount === 1
))
})
test('host gracefully handles guests switching to an editor that has already been destroyed', async () => {
const hostPeerPool = await buildPeerPool('host', server)
const guestPeerPool = await buildPeerPool('guest', server)
const hostPortal = await buildPortal('portal', hostPeerPool)
const guestPortal = await buildPortal('portal', guestPeerPool, 'host')
await guestPortal.join()
const hostBufferProxy1 = await hostPortal.createBufferProxy({uri: 'uri-1', text: ''})
const hostEditorProxy1 = await hostPortal.createEditorProxy({bufferProxy: hostBufferProxy1})
hostPortal.activateEditorProxy(hostEditorProxy1)
await condition(() => guestPortal.testDelegate.getTetherBufferProxyURI() === 'uri-1')
const guestEditorProxy1 = guestPortal.testDelegate.getTetherEditorProxy()
hostEditorProxy1.dispose()
guestPortal.activateEditorProxy(null)
guestPortal.activateEditorProxy(guestEditorProxy1)
const hostBufferProxy2 = await hostPortal.createBufferProxy({uri: 'uri-2', text: ''})
const hostEditorProxy2 = await hostPortal.createEditorProxy({bufferProxy: hostBufferProxy2})
hostPortal.activateEditorProxy(hostEditorProxy2)
await condition(() => guestPortal.getEditorProxiesMetadata().find((e) => e.bufferProxyURI === 'uri-2'))
})
})
async function buildPortal (portalId, peerPool, hostPeerId) {
const siteId = hostPeerId == null ? 1 : null
const portal = new Portal({id: portalId, hostPeerId, siteId, peerPool})
await portal.initialize()
portal.testDelegate = new FakePortalDelegate()
await portal.setDelegate(portal.testDelegate)
return portal
}
})