Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch unit tests to WebXR and fix vive(-focus)-controls #5452

Merged
merged 1 commit into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/components/vive-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var isWebXRAvailable = require('../utils/').device.isWebXRAvailable;
var GAMEPAD_ID_WEBXR = 'htc-vive';
var GAMEPAD_ID_WEBVR = 'OpenVR ';

// Prefix for Gen1 and Gen2 Oculus Touch Controllers.
// Prefix for HTC Vive Controllers.
var GAMEPAD_ID_PREFIX = isWebXRAvailable ? GAMEPAD_ID_WEBXR : GAMEPAD_ID_WEBVR;

/**
Expand Down Expand Up @@ -44,16 +44,16 @@ var INPUT_MAPPING_WEBVR = {
* Reference: https://github.com/immersive-web/webxr-input-profiles/blob/master/packages/registry/profiles/htc/htc-vive.json
*/
var INPUT_MAPPING_WEBXR = {
axes: {thumbstick: [0, 1]},
buttons: ['trigger', 'grip', 'trackpad', 'none', 'menu']
axes: {touchpad: [0, 1]},
buttons: ['trigger', 'grip', 'touchpad', 'none']
};

var INPUT_MAPPING = isWebXRAvailable ? INPUT_MAPPING_WEBXR : INPUT_MAPPING_WEBVR;

/**
* Vive controls.
* Interface with Vive controllers and map Gamepad events to controller buttons:
* trackpad, trigger, grip, menu, system
* touchpad, trigger, grip, menu, system
* Load a controller model and highlight the pressed buttons.
*/
module.exports.Component = registerComponent('vive-controls', {
Expand Down Expand Up @@ -209,6 +209,7 @@ module.exports.Component = registerComponent('vive-controls', {
buttonMeshes.menu = controllerObject3D.getObjectByName('menubutton');
buttonMeshes.system = controllerObject3D.getObjectByName('systembutton');
buttonMeshes.trackpad = controllerObject3D.getObjectByName('touchpad');
buttonMeshes.touchpad = controllerObject3D.getObjectByName('touchpad');
buttonMeshes.trigger = controllerObject3D.getObjectByName('trigger');

// Set default colors.
Expand Down
43 changes: 32 additions & 11 deletions src/components/vive-focus-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,40 @@ var checkControllerPresentAndSetup = trackedControlsUtils.checkControllerPresent
var emitIfAxesChanged = trackedControlsUtils.emitIfAxesChanged;
var onButtonEvent = trackedControlsUtils.onButtonEvent;

var GAMEPAD_ID_PREFIX = 'HTC Vive Focus';

var AFRAME_CDN_ROOT = require('../constants').AFRAME_CDN_ROOT;
var VIVE_FOCUS_CONTROLLER_MODEL_URL = AFRAME_CDN_ROOT + 'controllers/vive/focus-controller/focus-controller.gltf';

var isWebXRAvailable = require('../utils/').device.isWebXRAvailable;

var GAMEPAD_ID_WEBXR = 'htc-vive-focus';
var GAMEPAD_ID_WEBVR = 'HTC Vive Focus ';

// Prefix for HTC Vive Focus Controllers.
var GAMEPAD_ID_PREFIX = isWebXRAvailable ? GAMEPAD_ID_WEBXR : GAMEPAD_ID_WEBVR;

/**
* Button IDs:
* 0 - trackpad
* 1 - trigger
*/
var INPUT_MAPPING_WEBVR = {
axes: {trackpad: [0, 1]},
buttons: ['trackpad', 'trigger']
};

/**
* Button IDs:
* 0 - trigger
* 2 - touchpad
* 4 - menu
*/
var INPUT_MAPPING_WEBXR = {
axes: {touchpad: [0, 1]},
buttons: ['trigger', 'none', 'touchpad', 'none', 'menu']
};

var INPUT_MAPPING = isWebXRAvailable ? INPUT_MAPPING_WEBXR : INPUT_MAPPING_WEBVR;

/**
* Vive Focus controls.
* Interface with Vive Focus controller and map Gamepad events to
Expand All @@ -26,15 +55,7 @@ module.exports.Component = registerComponent('vive-focus-controls', {
armModel: {default: true}
},

/**
* Button IDs:
* 0 - trackpad
* 1 - trigger
*/
mapping: {
axes: {trackpad: [0, 1]},
buttons: ['trackpad', 'trigger']
},
mapping: INPUT_MAPPING,

bindMethods: function () {
this.onModelLoaded = this.onModelLoaded.bind(this);
Expand Down
2 changes: 1 addition & 1 deletion src/utils/device.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ var supportsARSession = false;
* Oculus Browser 7 doesn't support the WebXR gamepads module.
* We fallback to WebVR API and will hotfix when implementation is complete.
*/
var isWebXRAvailable = module.exports.isWebXRAvailable = !window.debug && navigator.xr !== undefined;
var isWebXRAvailable = module.exports.isWebXRAvailable = navigator.xr !== undefined;

// Catch vrdisplayactivate early to ensure we can enter VR mode after the scene loads.
window.addEventListener('vrdisplayactivate', function (evt) {
Expand Down
15 changes: 13 additions & 2 deletions tests/__init.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
/* global sinon, setup, teardown */
/* global EventTarget, sinon, setup, teardown */

/**
* __init.test.js is run before every test case.
*/
window.debug = true;

/* WebVR Stub */
navigator.getVRDisplays = function () {
var resolvePromise = Promise.resolve();
var mockVRDisplay = {
Expand All @@ -19,6 +20,16 @@ navigator.getVRDisplays = function () {
return Promise.resolve([mockVRDisplay]);
};

/* WebXR Stub */
navigator.xr = navigator.xr || {};
navigator.xr.isSessionSupported = function (_sessionType) { return Promise.resolve(true); };
navigator.xr.requestSession = function (_mode) {
const xrSession = new EventTarget();
xrSession.supportedFrameRates = [90];
xrSession.requestReferenceSpace = function () { return Promise.resolve(); };
return Promise.resolve(xrSession);
};

const AFRAME = require('index');
var AScene = require('core/scene/a-scene').AScene;
// Make sure WebGL context is not created since CI runs headless.
Expand All @@ -28,7 +39,7 @@ AScene.prototype.setupRenderer = function () {};
setup(function () {
window.AFRAME = AFRAME;
this.sinon = sinon.createSandbox();
// Stubs to not create a WebGL context since Travis CI runs headless.
// Stubs to not create a WebGL context since CI runs headless.
this.sinon.stub(AScene.prototype, 'render');
this.sinon.stub(AScene.prototype, 'setupRenderer');
// Mock renderer.
Expand Down
85 changes: 11 additions & 74 deletions tests/components/oculus-go-controls.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,9 @@ suite('oculus-go-controls', function () {
component.controllers = [];
// Our Mock data for enabling the controllers.
component.controllersWhenPresent = [{
id: 'Oculus Go Controller',
index: 0,
hand: 'right',
axes: [0, 0],
buttons: [
{value: 0, pressed: false, touched: false},
{value: 0, pressed: false, touched: false}
],
pose: {orientation: [1, 0, 0, 0], position: null}
profiles: ['oculus-go'],
handedness: 'right'
}];
el.parentEl.renderer.xr.getStandingMatrix = function () {};
done();
};
if (el.hasLoaded) { callback(); }
Expand All @@ -37,7 +29,7 @@ suite('oculus-go-controls', function () {

setup(function (done) {
component = this.el.components['oculus-go-controls'];
controllerSystem = this.el.sceneEl.systems['tracked-controls-webvr'];
controllerSystem = this.el.sceneEl.systems['tracked-controls-webxr'];
addEventListenersSpy = sinon.spy(component, 'addEventListeners');
injectTrackedControlsSpy = sinon.spy(component, 'injectTrackedControls');
removeEventListenersSpy = sinon.spy(component, 'removeEventListeners');
Expand Down Expand Up @@ -117,14 +109,14 @@ suite('oculus-go-controls', function () {
});

suite('axismove', function () {
test('emits trackpadmoved on axismove', function (done) {
test('emits touchpadmoved on axismove', function (done) {
var el = this.el;
setupTestControllers(el);

// Configure the event state for which we'll use the axis state for verification.
const eventState = {axis: [0.1, 0.2], changed: [true, false]};

el.addEventListener('trackpadmoved', function (evt) {
el.addEventListener('touchpadmoved', function (evt) {
assert.equal(evt.detail.x, eventState.axis[0]);
assert.equal(evt.detail.y, eventState.axis[1]);
done();
Expand All @@ -133,13 +125,13 @@ suite('oculus-go-controls', function () {
el.emit('axismove', eventState);
});

test('does not emit trackpadmoved on axismove with no changes', function (done) {
test('does not emit touchpadmoved on axismove with no changes', function (done) {
var el = this.el;
setupTestControllers(el);

// Fail purposely.
el.addEventListener('trackpadmoved', function (evt) {
assert.fail('trackpadmoved was called when there was no change.');
el.addEventListener('touchpadmoved', function (evt) {
assert.fail('touchpadmoved was called when there was no change.');
});

el.emit('axismove', {axis: [0.1, 0.2], changed: [false, false]});
Expand All @@ -148,8 +140,8 @@ suite('oculus-go-controls', function () {
});

suite('buttonchanged', function () {
[{ button: 'trackpad', id: 0 },
{ button: 'trigger', id: 1 }
[{ button: 'trigger', id: 0 },
{ button: 'touchpad', id: 2 }
].forEach(function (buttonDescription) {
test('if we get buttonchanged for button ' + buttonDescription.id + ', emit ' + buttonDescription.button + 'changed', function (done) {
var el = this.el;
Expand Down Expand Up @@ -190,69 +182,14 @@ suite('oculus-go-controls', function () {
});
});

suite('armModel', function () {
test('does not apply armModel if armModel disabled', function () {
var el = this.el;
el.setAttribute('oculus-go-controls', 'armModel', false);
setupTestControllers(el);

var trackedControls = el.components['tracked-controls-webvr'];
var applyArmModelSpy = sinon.spy(trackedControls, 'applyArmModel');
trackedControls.tick();

// Verify that the function which applies arm model is not called when disabled.
sinon.assert.notCalled(applyArmModelSpy);

// Additionally verify that no other offsets have been applied.
assert.strictEqual(el.object3D.position.x, 0);
assert.strictEqual(el.object3D.position.y, 0);
assert.strictEqual(el.object3D.position.z, 0);
});

test('applies armModel if armModel enabled', function () {
var el = this.el;
el.setAttribute('oculus-go-controls', 'armModel', true);
setupTestControllers(el);

var trackedControls = el.components['tracked-controls-webvr'];
var applyArmModelSpy = sinon.spy(trackedControls, 'applyArmModel');
trackedControls.tick();

// Verify that the function which applies arm model is called.
sinon.assert.calledOnce(applyArmModelSpy);
});

test('verifies armModel position is applied for the right hand', function () {
var el = this.el;
el.setAttribute('oculus-go-controls', 'armModel', true);
setupTestControllers(el);

var trackedControls = el.components['tracked-controls-webvr'];
trackedControls.tick();
assert.ok(el.object3D.position.x > 0);
});

test('verifies armModel position is applied for the left hand', function () {
var el = this.el;
el.setAttribute('oculus-go-controls', 'armModel', true);
el.setAttribute('oculus-go-controls', 'hand', 'left');
el.components['oculus-go-controls'].controllersWhenPresent[0].hand = 'left';
setupTestControllers(el);

var trackedControls = el.components['tracked-controls-webvr'];
trackedControls.tick();
assert.ok(el.object3D.position.x < 0);
});
});

/**
* Establishes the baseline set of controllers needed for the tests to run.
*
* @param {object} el - The current entity factory.
*/
function setupTestControllers (el) {
var component = el.components['oculus-go-controls'];
el.sceneEl.systems['tracked-controls-webvr'].controllers = component.controllersWhenPresent;
el.sceneEl.systems['tracked-controls-webxr'].controllers = component.controllersWhenPresent;
component.checkIfControllerPresent();
}
});
23 changes: 10 additions & 13 deletions tests/components/oculus-touch-controls.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@ suite('oculus-touch-controls', function () {
component.controllers = [];
// Our Mock data for enabling the controllers.
component.controllersWhenPresent = [{
id: 'Oculus Touch',
index: 0,
hand: 'left',
pose: {}
profiles: ['oculus-touch'],
handedness: 'left'
}];
done();
};
Expand All @@ -34,7 +32,7 @@ suite('oculus-touch-controls', function () {

setup(function (done) {
component = this.el.components['oculus-touch-controls'];
controllerSystem = this.el.sceneEl.systems['tracked-controls-webvr'];
controllerSystem = this.el.sceneEl.systems['tracked-controls-webxr'];
controllerSystem.vrDisplay = true;
addEventListenersSpy = sinon.spy(component, 'addEventListeners');
injectTrackedControlsSpy = sinon.spy(component, 'injectTrackedControls');
Expand Down Expand Up @@ -118,7 +116,7 @@ suite('oculus-touch-controls', function () {
var controllerSystem;

setup(function (done) {
controllerSystem = this.el.sceneEl.systems['tracked-controls-webvr'];
controllerSystem = this.el.sceneEl.systems['tracked-controls-webxr'];
controllerSystem.controllers = component.controllersWhenPresent;
controllerSystem.vrDisplay = true;
done();
Expand All @@ -128,11 +126,11 @@ suite('oculus-touch-controls', function () {
// Do the check.
component.checkIfControllerPresent();
// Set up the event details.
const eventDetails = {axis: [0.1, 0.2], changed: [true, false]};
const eventDetails = {axis: [0.1, 0.2, 0.3, 0.4], changed: [false, false, true, false]};
// Install event handler listening for thumbstickmoved.
this.el.addEventListener('thumbstickmoved', function (evt) {
assert.equal(evt.detail.x, eventDetails.axis[0]);
assert.equal(evt.detail.y, eventDetails.axis[1]);
assert.equal(evt.detail.x, eventDetails.axis[2]);
assert.equal(evt.detail.y, eventDetails.axis[3]);
done();
});
// Emit axismove.
Expand All @@ -147,15 +145,14 @@ suite('oculus-touch-controls', function () {
assert.fail('thumbstickmoved should not be called');
});
// Emit axismove with no changes.
this.el.emit('axismove', {axis: [0.1, 0.2], changed: [false, false]});
this.el.emit('axismove', {axis: [0.1, 0.2, 0.3, 0.4], changed: [false, false, false, false]});
setTimeout(() => { done(); });
});
});

suite('buttonchanged', function () {
test('can emit triggerchanged', function (done) {
el.sceneEl.systems['tracked-controls-webvr'].controllers = component.controllersWhenPresent;
el.sceneEl.systems['tracked-controls-webvr'].vrDisplay = true;
el.sceneEl.systems['tracked-controls-webxr'].controllers = component.controllersWhenPresent;
// Do the check.
component.checkIfControllerPresent();
// Prepare the event details
Expand All @@ -166,7 +163,7 @@ suite('oculus-touch-controls', function () {
done();
});
// Emit buttonchanged.
el.emit('buttonchanged', {id: 1, state: eventState});
el.emit('buttonchanged', {id: 0, state: eventState});
});
});
});
8 changes: 0 additions & 8 deletions tests/components/scene/xr-mode-ui.test.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
/* global assert, process, setup, suite, test */
var entityFactory = require('../../helpers').entityFactory;
var utils = require('index').utils;

var UI_CLASSES = ['.a-orientation-modal', '.a-enter-vr-button'];

suite('xr-mode-ui', function () {
setup(function (done) {
this.entityEl = entityFactory();
var el = this.el = this.entityEl.parentNode;
this.sinon.stub(utils.device, 'getVRDisplay').returns({
requestPresent: function () {
return Promise.resolve();
}
});
el.addEventListener('loaded', function () { done(); });
});

Expand All @@ -38,7 +32,6 @@ suite('xr-mode-ui', function () {
el: {object3D: {}},
updateProjectionMatrix: function () {}
};
window.hasNativeWebVRImplementation = false;
scene.enterVR();
UI_CLASSES.forEach(function (uiClass) {
assert.ok(scene.querySelector(uiClass).className.indexOf('a-hidden'));
Expand All @@ -52,7 +45,6 @@ suite('xr-mode-ui', function () {
el: {object3D: {}, getAttribute: function () { return {spectator: false}; }},
updateProjectionMatrix: function () {}
};
window.hasNativeWebVRImplementation = false;
scene.enterVR();
scene.exitVR();

Expand Down
Loading
Loading