text: /* global assert, process, sinon, setup, suite, teardown, test, THREE */
  'use strict';
  var helpers = require('../helpers');
  var AEntity = require('core/a-entity');
  var ANode = require('core/a-node');
  var AScene = require('core/scene/a-scene');
  
  /**
   * Tests in this suite should not involve WebGL contexts or renderer.
   * They operate with the assumption that attachedCallback is stubbed.
   *
   * Add tests that involve the renderer to the suite at the bottom that is meant
   * to only be run locally since WebGL contexts break CI due to the headless
   * environment.
   */
  suite('a-scene (without renderer)', function () {
    setup(function () {
      var el;
      el = this.el = document.createElement('a-scene');
      document.body.appendChild(el);
    });
  
    teardown(function () {
      document.body.removeChild(this.el);
    });
  
    suite('createdCallback', function () {
      test('initializes scene object', function () {
        assert.equal(this.el.object3D.type, 'Scene');
      });
    });
  
    suite('init', function () {
      test('initializes scene object', function () {
        var sceneEl = this.el;
        sceneEl.behaviors = ['dummy'];
        sceneEl.materials = {dummy: 1};
        sceneEl.isPlaying = false;
        sceneEl.hasLoaded = true;
        sceneEl.init();
        assert.equal(sceneEl.behaviors.length, 0);
        assert.equal(Object.keys(sceneEl.materials).length, 0);
        assert.equal(sceneEl.isPlaying, true);
        assert.equal(sceneEl.hasLoaded, false);
      });
    });
  
    suite('reload', function () {
      test('reload scene innerHTML to original value', function () {
        var sceneEl = this.el;
        sceneEl.innerHTML = 'NEW';
        sceneEl.reload();
        assert.ok(sceneEl.innerHTML, '');
      });
  
      test('reloads the scene and pauses', function () {
        var sceneEl = this.el;
        this.sinon.spy(AEntity.prototype, 'pause');
        this.sinon.spy(ANode.prototype, 'load');
        sceneEl.reload(true);
        sinon.assert.called(AEntity.prototype.pause);
        sinon.assert.called(ANode.prototype.load);
      });
    });
  
    suite('setActiveCamera', function () {
      test('sets new active camera in three.js graph', function () {
        var el = this.el;
        var camera = new THREE.PerspectiveCamera(45, 2, 1, 1000);
        el.setActiveCamera(camera);
        assert.equal(el.camera, camera);
      });
  
      test('sets new active camera in entity graph', function (done) {
        var self = this;
        var cameraEl = document.createElement('a-entity');
        cameraEl.setAttribute('camera', '');
        this.el.appendChild(cameraEl);
        process.nextTick(function () {
          self.el.setActiveCamera(cameraEl);
          assert.equal(self.el.camera, cameraEl.components.camera.camera);
          done();
        });
      });
    });
  
    suite('setActiveCamera', function () {
      test('switches active camera', function (done) {
        var sceneEl = this.el;
        var camera1El = document.createElement('a-entity');
        var camera2El = document.createElement('a-entity');
        camera1El.setAttribute('camera', 'active: false');
        sceneEl.appendChild(camera1El);
        camera2El.setAttribute('camera', 'active: true');
        sceneEl.appendChild(camera2El);
        process.nextTick(function () {
          assert.equal(camera1El.getAttribute('camera').active, false);
          sceneEl.setActiveCamera(camera1El);
          assert.equal(camera1El.getAttribute('camera').active, true);
          assert.equal(camera2El.getAttribute('camera').active, false);
          done();
        });
      });
    });
  
    suite('removeDefaultCamera', function () {
      test('removes the default camera', function (done) {
        var sceneEl = this.el;
        var defaultCamera;
        sceneEl.setupDefaultCamera();
        process.nextTick(function () {
          defaultCamera = sceneEl.querySelector('[data-aframe-default-camera]');
          assert.notEqual(defaultCamera, null);
          // Mocks camera initialization
          sceneEl.camera = { el: true };
          sceneEl.removeDefaultCamera();
          defaultCamera = sceneEl.querySelector('[data-aframe-default-camera]');
          assert.equal(defaultCamera, null);
          done();
        });
      });
    });
  
    suite('updateCameras', function () {
      test('disable inactive cameras', function (done) {
        var sceneEl = this.el;
        var cameraEl = document.createElement('a-entity');
        cameraEl.setAttribute('camera', 'active: false');
        sceneEl.appendChild(cameraEl);
  
        var camera2El = document.createElement('a-entity');
        camera2El.setAttribute('camera', 'active: false');
        sceneEl.appendChild(camera2El);
  
        process.nextTick(function () {
          cameraEl.setAttribute('camera', 'active: true');
          camera2El.setAttribute('camera', 'active: true');
          assert.equal(cameraEl.getAttribute('camera').active, false);
          assert.equal(camera2El.getAttribute('camera').active, true);
          assert.equal(camera2El.components.camera.camera, sceneEl.camera);
          done();
        });
      });
    });
  
    suite('setupDefaultLights', function () {
      setup(function () {
        var el = this.el;
        var lights = el.querySelectorAll('[light]');
        var i;
        for (i = 0; i < lights.length; ++i) {
          el.removeChild(lights[i]);
        }
      });
  
      test('adds lights to scene', function (done) {
        var el = this.el;
        assert.notOk(document.querySelectorAll('[light]').length);
        el.setupDefaultLights();
        process.nextTick(function () {
          assert.ok(document.querySelectorAll('[light]').length);
          done();
        });
      });
  
      test('removes default lights when more lights are added', function (done) {
        var el = this.el;
        var light = document.createElement('a-entity');
        light.setAttribute('light', '');
  
        el.setupDefaultLights();
        process.nextTick(function () {
          el.appendChild(light);
          setTimeout(function () {
            assert.notOk(
              document.querySelectorAll('[data-aframe-default-light]').length);
            done();
          });
        });
      });
    });
  });
  
  /**
   * Skipped on CI using environment variable defined in the npm test script.
   */
  helpers.getSkipCISuite()('a-scene (with renderer)', function () {
    setup(function (done) {
      var el;
      var self = this;
      AScene.prototype.setupRenderer.restore();
      AScene.prototype.resize.restore();
      AScene.prototype.render.restore();
      process.nextTick(function () {
        el = self.el = document.createElement('a-scene');
        document.body.appendChild(el);
        el.addEventListener('renderstart', function () {
          done();
        });
      });
    });
  
    suite('attachedCallback', function () {
      test('sets up meta tags', function () {
        assert.ok(document.querySelector('meta[name="viewport"]'));
      });
  
      test('sets up renderer', function () {
        assert.ok(this.el.renderer);
      });
    });
  
    suite('detachedCallback', function () {
      test('cancels request animation frame', function (done) {
        var el = this.el;
        var animationFrameID = el.animationFrameID;
        var cancelSpy = this.sinon.spy(window, 'cancelAnimationFrame');
  
        assert.ok(el.animationFrameID);
        document.body.removeChild(el);
        process.nextTick(function () {
          assert.notOk(el.animationFrameID);
          assert.ok(cancelSpy.calledWith(animationFrameID));
          done();
        });
      });
  
      test('does not destroy document.body', function (done) {
        var el = this.el;
        document.body.removeChild(el);
        process.nextTick(function () {
          assert.ok(document.body);
          done();
        });
      });
    });
  
    test('calls behaviors', function () {
      var scene = this.el;
      var Component = { el: { isPlaying: true }, tick: function () {} };
      this.sinon.spy(Component, 'tick');
      scene.addBehavior(Component);
      scene.render();
      sinon.assert.called(Component.tick);
      sinon.assert.calledWith(Component.tick, scene.time);
    });
  });
  