UNPKG

react-aframe-ar

Version:

Build virtual and augmented reality experiences with React and A-Frame.

405 lines (368 loc) 13.6 kB
import React from 'react'; import ReactDOM from 'react-dom'; import {Entity, Scene} from '../../src/index.js'; const div = document.createElement('div'); document.body.appendChild(div); teardown(function () { while (div.firstChild) { div.removeChild(div.firstChild); } }); suite('react-aframe', () => { test('renders scene and entity', () => { ReactDOM.render(<Scene><Entity/></Scene>, div); assert.ok(div.querySelector('a-scene a-entity')); }); test('renders nested entity', () => { ReactDOM.render(<Scene><Entity><Entity/></Entity></Scene>, div); assert.ok(div.querySelector('a-scene a-entity a-entity')); }); test('sets id', () => { ReactDOM.render(<Scene id="scene"><Entity id="entity"/></Scene>, div); assert.ok(div.querySelector('a-scene#scene')); assert.ok(div.querySelector('a-entity#entity')); }); test('sets class', () => { ReactDOM.render(<Scene className="scene"><Entity className="entity"/></Scene>, div); assert.ok(div.querySelector('a-scene.scene')); assert.ok(div.querySelector('a-entity.entity')); }); test('sets mixin', () => { ReactDOM.render(<Scene><Entity mixin="box"/></Scene>, div); assert.ok(div.querySelector('a-entity[mixin="box"]')); }); test('initializes component to entity from string', done => { ReactDOM.render(<Scene><Entity position="1 2 3"/></Scene>, div); div.querySelector('a-scene').addEventListener('loaded', () => { assert.shallowDeepEqual(div.querySelector('a-entity').getAttribute('position'), {x: 1, y: 2, z: 3}); done(); }); }); test('initializes component to entity from object', done => { ReactDOM.render(<Scene><Entity position={{x: 1, y: 2, z: 3}}/></Scene>, div); div.querySelector('a-scene').addEventListener('loaded', () => { assert.shallowDeepEqual(div.querySelector('a-entity').getAttribute('position'), {x: 1, y: 2, z: 3}); done(); }); }); test('initializes geometry component to entity from object', done => { ReactDOM.render(<Scene><Entity geometry={{primitive: 'sphere', radius: 2}}/></Scene>, div); div.querySelector('a-scene').addEventListener('loaded', () => { assert.shallowDeepEqual(div.querySelector('a-entity').getAttribute('geometry'), {primitive: 'sphere', radius: 2}); done(); }); }); test('updates entity with new props', done => { ReactDOM.render(<Scene><Entity scale={{x: 1, y: 2, z: 3}}/></Scene>, div); div.querySelector('a-scene').addEventListener('loaded', () => { const el = div.querySelector('a-entity'); assert.shallowDeepEqual(el.getAttribute('scale'), {x: 1, y: 2, z: 3}); // Re-rendering with different props will update and not re-mount. ReactDOM.render(<Scene><Entity scale={{x: 2, y: 3, z: 4}}/></Scene>, div); assert.shallowDeepEqual(el.getAttribute('scale'), {x: 2, y: 3, z: 4}); done(); }); }); test('updates deeply-nested entity with new props', done => { ReactDOM.render( <Scene> <Entity> <Entity> <Entity id="el" geometry={{primitive: 'cylinder', height: 5}}/> </Entity> </Entity> </Scene>, div ); div.querySelector('a-scene').addEventListener('loaded', () => { ReactDOM.render( <Scene> <Entity> <Entity> <Entity id="el" geometry={{primitive: 'cylinder', height: 25, radius: 5}}/> </Entity> </Entity> </Scene>, div ); setTimeout(() => { const geometry = div.querySelector('#el').getAttribute('geometry'); assert.equal(geometry.primitive, 'cylinder'); assert.equal(geometry.height, 25); assert.equal(geometry.radius, 5); done(); }); }); }); test('renders entity wrapped in React component', done => { class Camera extends React.Component { render() { return ( <Entity id='cameraContainer'> <Entity id='camera' camera> <Entity id='cursor' cursor={{fuse: true, fuseTimeout: 3000}} raycaster={{objects: '.foo'}} geometry={{primitive: 'plane'}}/> </Entity> </Entity> ); } } ReactDOM.render(<Scene><Camera/></Scene>, div); div.querySelector('a-scene').addEventListener('loaded', () => { assert.ok(div.querySelector('#camera').getAttribute('camera'), 'Has camera'); assert.ok(div.querySelector('#cursor').getAttribute('cursor'), 'Has cursor'); assert.ok(div.querySelector('#cursor').getAttribute('raycaster'), 'Has raycaster'); done(); }); }); test('can attach new entities', done => { ReactDOM.render( <Scene> <Entity id='sphere' geometry={{primitive: 'sphere'}} material={{color: 'blue'}}/> </Scene>, div ); div.querySelector('a-scene').addEventListener('loaded', () => { ReactDOM.render( <Scene> <Entity id='sphere' geometry={{primitive: 'sphere'}} material={{color: 'blue'}}> <Entity id='torus' geometry={{primitive: 'torus'}} material={{color: 'orange'}}/> </Entity> </Scene>, div ); setTimeout(() => { const sphere = div.querySelector('#sphere'); const torus = div.querySelector('#torus'); assert.equal(sphere.getAttribute('geometry').primitive, 'sphere'); assert.equal(sphere.getAttribute('material').color, 'blue'); assert.equal(torus.getAttribute('geometry').primitive, 'torus'); assert.equal(torus.getAttribute('material').color, 'orange'); done(); }); }); }); test('can detach entities', done => { ReactDOM.render( <Scene> <Entity id='foo'> <Entity id='bar'/> </Entity> </Scene>, div ); div.querySelector('a-scene').addEventListener('loaded', () => { ReactDOM.render( <Scene> <Entity id='foo'/> </Scene>, div ); setTimeout(() => { assert.ok(div.querySelector('#foo')); assert.notOk(div.querySelector('#bar')); done(); }); }); }); test('does not flush props to DOM', done => { ReactDOM.render(<Scene><Entity position={{x: 1, y: 2, z: 3}}/></Scene>, div); div.querySelector('a-scene').addEventListener('loaded', () => { assert.notOk(HTMLElement.prototype.getAttribute.call( div.querySelector('a-entity'), 'position')); done(); }); }); }); suite('<Entity primitive/>', () => { test('renders <a-box>', () => { ReactDOM.render(<Scene><Entity primitive='a-box'/></Scene>, div); assert.ok(div.querySelector('a-scene a-box')); }); test('renders <a-box> with mapping', done => { ReactDOM.render(<Scene><Entity primitive='a-box' color="red"/></Scene>, div); const box = div.querySelector('a-scene a-box'); assert.ok(box); div.querySelector('a-scene').addEventListener('loaded', () => { assert.equal(box.getAttribute('color'), 'red'); assert.equal(box.getAttribute('geometry').primitive, 'box'); done(); }); }); test('handles overrides with updating component', done => { ReactDOM.render(<Scene><Entity primitive='a-cylinder' geometry={{height: 5}}/></Scene>, div); const cylinderEl = div.querySelector('a-cylinder'); div.querySelector('a-scene').addEventListener('loaded', () => { assert.equal(cylinderEl.getAttribute('geometry').primitive, 'cylinder'); assert.equal(cylinderEl.getAttribute('geometry').height, 5); ReactDOM.render(<Scene><Entity primitive='a-cylinder' geometry={{height: 10}}/></Scene>, div); assert.equal(cylinderEl.getAttribute('geometry').primitive, 'cylinder'); assert.equal(cylinderEl.getAttribute('geometry').height, 10); done(); }); }); test('supports kebab-case', done => { ReactDOM.render(<Scene><Entity primitive='a-box' segments-depth={5}/></Scene>, div); div.querySelector('a-scene').addEventListener('loaded', () => { assert.equal(div.querySelector('a-scene a-box').getAttribute('geometry').segmentsDepth, 5); done(); }); }); test('supports null or undefined props', () => { ReactDOM.render(<Scene undefined={undefined} null={null} />, div); const scene = div.querySelector('a-scene'); assert.equal(scene.getAttribute('undefined'), 'undefined'); assert.equal(scene.getAttribute('null'), 'null'); }); }); suite('<Entity events/>', () => { test('adds event listener', function (done) { let handlerCalled = false; function handler () { handlerCalled = true } ReactDOM.render(<Scene><Entity events={{foo: handler}}/></Scene>, div); div.querySelector('a-scene').addEventListener('loaded', () => { div.querySelector('a-entity').emit('foo'); setTimeout(() => { assert.ok(handlerCalled); done(); }); }); }); test('can add multiple event listeners', function (done) { let fooHandlerCalled = false; let barHandlerCalled = false; function fooHandler () { fooHandlerCalled = true } function barHandler () { barHandlerCalled = true } ReactDOM.render(<Scene><Entity events={{foo: [fooHandler, barHandler]}}/></Scene>, div); div.querySelector('a-scene').addEventListener('loaded', () => { div.querySelector('a-entity').emit('foo'); setTimeout(() => { assert.ok(fooHandlerCalled); assert.ok(barHandlerCalled); done(); }); }); }); test('can replace event listeners', function (done) { let fooHandlerCalled = false; let barHandlerCalled = false; function fooHandler () { fooHandlerCalled = true } function barHandler () { barHandlerCalled = true } ReactDOM.render(<Scene><Entity events={{foo: fooHandler}}/></Scene>, div); ReactDOM.render(<Scene><Entity events={{bar: barHandler}}/></Scene>, div); div.querySelector('a-scene').addEventListener('loaded', () => { div.querySelector('a-entity').emit('foo'); div.querySelector('a-entity').emit('bar'); setTimeout(() => { assert.notOk(fooHandlerCalled); assert.ok(barHandlerCalled); done(); }); }); }); test('can remove event listener', function (done) { let fooHandlerCalled = false; function fooHandler () { fooHandlerCalled = true } ReactDOM.render(<Scene><Entity events={{foo: fooHandler}}/></Scene>, div); ReactDOM.render(<Scene><Entity events={{}}/></Scene>, div); div.querySelector('a-scene').addEventListener('loaded', () => { div.querySelector('a-entity').emit('foo'); setTimeout(() => { assert.notOk(fooHandlerCalled); done(); }); }); }); test('can remove event listener when component is detached', function (done) { let bazHandlerCalled = false; function bazHandler () { bazHandlerCalled = true } ReactDOM.render( <Scene> <Entity id='foo'> <Entity id='bar' events={{baz: bazHandler}}/> </Entity> </Scene>, div ); div.querySelector('a-scene').addEventListener('loaded', () => { ReactDOM.render( <Scene> <Entity id='foo'/> </Scene>, div ); div.querySelector('a-entity').emit('baz'); setTimeout(() => { assert.ok(div.querySelector('#foo')); assert.notOk(div.querySelector('#bar')); assert.notOk(bazHandlerCalled); done(); }); }); }); test('can attach extra event listener', function (done) { let fooHandlerCalled = false; let barHandlerCalled = false; function fooHandler () { fooHandlerCalled = true } function barHandler () { barHandlerCalled = true } ReactDOM.render(<Scene><Entity events={{foo: fooHandler, bar: barHandler}}/></Scene>, div); div.querySelector('a-scene').addEventListener('loaded', () => { div.querySelector('a-entity').emit('foo'); div.querySelector('a-entity').emit('bar'); setTimeout(() => { assert.ok(fooHandlerCalled); assert.ok(barHandlerCalled); done(); }); }); }); test('can remove attribute', function (done) { ReactDOM.render( <Scene> <Entity light={{ type: "ambient", color: "#CCC" }} /> </Scene>, div ); ReactDOM.render( <Scene> <Entity /> </Scene>, div ); const scene = div.querySelector('a-scene'); scene.addEventListener('loaded', () => { setTimeout(() => { assert.equal(div.querySelector('a-entity').hasAttribute('light'), false); done(); }); }); }); }); suite('<Scene/>', () => { test('can take single-property boolean component as boolean', done => { ReactDOM.render(<Scene true={true} false={false}/>, div); const scene = div.querySelector('a-scene'); scene.addEventListener('loaded', () => { assert.equal(scene.getAttribute('true'), 'true'); assert.equal(scene.getAttribute('false'), 'false'); done(); }); }); test('can take single-property boolean component as string', done => { ReactDOM.render(<Scene true="true" false="false"/>, div); const scene = div.querySelector('a-scene'); scene.addEventListener('loaded', () => { assert.equal(scene.getAttribute('true'), 'true'); assert.equal(scene.getAttribute('false'), 'false'); done(); }); }); });