networked-aframe
Version:
A web framework for building multi-user virtual reality experiences.
241 lines (184 loc) • 6.65 kB
JavaScript
/* global assert, setup, suite, test, teardown, sinon, THREE */
var sinonTest = require("sinon-test");
sinon.test = sinonTest(sinon);
require('aframe');
var helpers = require('./helpers');
var naf = require('../../src/NafIndex');
require('../../src/components/networked');
suite('networked', function() {
var scene;
var entity;
var networked;
var networkedSystem;
function initScene(done) {
var opts = {
assets: [
"<template id='t1'><a-entity><a-entity class='template-child'></a-entity></a-entity></template>"
],
entity: '<a-entity networked="template:#t1" position="1 2 3" rotation="4 3.5 2"><a-box></a-box></a-entity>'
};
scene = helpers.sceneFactory(opts);
naf.utils.whenEntityLoaded(scene, done);
}
setup(function(done) {
naf.clientId = 'owner1';
naf.connection.setNetworkAdapter(new helpers.MockNetworkAdapter());
initScene(function() {
entity = document.querySelector('a-entity');
networked = entity.components['networked'];
networkedSystem = scene.systems['networked'];
networked.data.networkId = '';
done();
});
});
teardown(function() {
scene.parentElement.removeChild(scene);
});
suite('Setup', function() {
test('creates entity', function() {
assert.isOk(entity);
});
test('creates component', function() {
assert.isOk(networked);
});
});
suite('init', function() {
test('sets networkId and element id', sinon.test(function() {
this.stub(naf.utils, 'createNetworkId').returns('nid1');
networked.el.id = ''
networked.init();
var result = networked.data.networkId;
assert.equal(result, 'nid1');
assert.equal(entity.id, 'naf-nid1');
}));
test('retains networkId after component update', sinon.test(function() {
this.stub(naf.utils, 'createNetworkId').returns('nid-after-load');
networked.init();
// A-Frame can call updateComponents for mulitple reasons. This can result in component data being rebuilt.
entity.updateComponents();
assert.equal(networked.data.networkId, 'nid-after-load');
}));
test('sets owner', sinon.test(function() {
networked.init();
document.body.dispatchEvent(new Event('loggedIn'));
var result = networked.data.owner;
assert.equal(result, 'owner1');
}));
test('sets null parent when has no parent', function() {
naf.clientId = 'owner1';
networked.init();
document.body.dispatchEvent(new Event('loggedIn'));
var result = networked.parent;
assert.isNull(result);
});
test('registers entity', sinon.test(function() {
var networkId = 'nid2';
this.stub(naf.utils, 'createNetworkId').returns(networkId);
var stub = this.stub(naf.entities, 'registerEntity');
networked.init();
assert.isTrue(stub.calledWith('nid2', entity));
}));
test('attaches local template', function() {
var templateChild = entity.querySelector('.template-child');
assert.isOk(templateChild);
});
});
suite('tick', function() {
test('syncs if need to', sinon.test(function() {
this.stub(networked, 'syncDirty');
networkedSystem.el.clock.elapsedTime = 4;
networkedSystem.nextSyncTime = 4;
networkedSystem.tick();
assert.isTrue(networked.syncDirty.calledOnce);
}));
test('does not sync if does not need to', sinon.test(function() {
this.stub(networked, 'syncDirty');
networked.el.sceneEl.clock.elapsedTime = 3.9;
networked.nextSyncTime = 4;
networked.tick();
assert.isFalse(networked.syncDirty.calledOnce);
}));
});
suite('syncAll', function() {
test('broadcasts uncompressed data', sinon.test(function() {
this.stub(naf.utils, 'createNetworkId').returns('network1');
this.stub(naf.connection, 'broadcastDataGuaranteed');
var expected = {
networkId: 'network1',
owner: 'owner1',
creator: 'owner1',
lastOwnerTime: -1,
template: '#t1',
persistent: false,
parent: null,
components: {
0: new THREE.Vector3(1, 2, 3),
1: { x: 4, y: 3.5, z: 2 }
},
isFirstSync: false
};
networked.init();
document.body.dispatchEvent(new Event('loggedIn'));
networked.syncAll();
var called = naf.connection.broadcastDataGuaranteed.calledWithExactly('u', expected);
assert.isTrue(called);
}));
});
suite('syncDirty', function() {
test('syncs data that has changed', sinon.test(function() {
this.stub(naf.utils, 'createNetworkId').returns('network1');
this.stub(naf.connection, 'broadcastData');
var expected = { d: [{
networkId: 'network1',
creator: 'owner1',
owner: 'owner1',
lastOwnerTime: -1,
persistent: false,
parent: null,
template: '#t1',
components: {
1: { x: 9, y: 8, z: 7 }
},
isFirstSync: false
}] };
networkedSystem.init();
networked.init();
// Force initial sync instead of waiting on onConnected
networked.syncAll();
networked.el.setAttribute("rotation", { x: 9, y: 8, z: 7 });
networkedSystem.el.clock.elapsedTime = 4;
networkedSystem.tick();
var called = naf.connection.broadcastData.calledWithExactly('um', expected);
assert.isTrue(called, `called with ${JSON.stringify(naf.connection.broadcastData.getCall(0).args[1])}, expected ${JSON.stringify(expected)}`);
}));
test('sets next sync time', sinon.test(function() {
this.stub(naf.connection, 'broadcastData');
this.spy(networkedSystem, 'updateNextSyncTime');
networkedSystem.init();
networkedSystem.tick();
assert.isTrue(networkedSystem.updateNextSyncTime.calledOnce);
}));
});
suite('gatherComponentsData', function() {
test('get position and rotation on full sync of root', function () {
var result = networked.gatherComponentsData(true);
var expected = {
0: new THREE.Vector3(1,2,3),
1: { x: 4, y: 3.5, z: 2 }
};
assert.deepEqual(result, expected);
});
test('get position and rotation on dirty sync of root', function () {
networked.cachedData = [
{ x: 1, y: 2, z: 3 },
{ x: 4, y: 3.5, z: 2 }
];
networked.el.setAttribute("rotation", { x: 9, y: 8, z: 7 });
var result = networked.gatherComponentsData(false);
var expected = {
1: { x: 9, y: 8, z: 7 }
};
assert.deepEqual(result, expected);
});
});
});