text: /* global MockAppWindow, AppStatusbar, MocksHelper, MockTouchForwarder */
  'use strict';
  
  require('/shared/test/unit/mocks/mock_l10n.js');
  requireApp('system/test/unit/mock_app_window.js');
  require('/test/unit/mock_touch_forwarder.js');
  requireApp('system/js/service.js');
  requireApp('system/js/base_ui.js');
  requireApp('system/js/app_statusbar.js');
  
  var mocksForAppStatusbar = new MocksHelper([
    'AppWindow',
    'TouchForwarder'
  ]).init();
  
  suite('system/AppStatusbar', function() {
    mocksForAppStatusbar.attachTestHelpers();
    var app;
    var subject;
    setup(function() {
      this.sinon.useFakeTimers();
      app = new MockAppWindow({ chrome: { navigation: false }});
      subject = new AppStatusbar(app);
      this.sinon.stub(app, 'isFullScreen').returns(true);
      subject.screen = document.createElement('div');
      subject.chromeBar = document.createElement('div');
      subject.titleBar = document.createElement('div');
    });
  
    suite('Reset destination if browser is destroyed', function() {
      test('Suspend', function() {
        app.element.dispatchEvent(new CustomEvent('_suspended'));
        assert.isNull(subject._touchForwarder.destination);
      });
  
      test('Resume', function() {
        var newBrowserElement = document.createElement('iframe');
        app.browser = {
          element: newBrowserElement
        };
        app.element.dispatchEvent(new CustomEvent('_resumed'));
        assert.deepEqual(subject._touchForwarder.destination, newBrowserElement);
      });
    });
  
    suite('fullscreen mode >', function() {
      function forgeTouchEvent(type, x, y) {
        var touch = document.createTouch(window, null, 42, x, y,
                                         x, y, x, y,
                                         0, 0, 0, 0);
        var touchList = document.createTouchList(touch);
        var touches = (type == 'touchstart' || type == 'touchmove') ?
                           touchList : null;
        var changed = (type == 'touchmove') ?
                           null : touchList;
  
        var e = document.createEvent('TouchEvent');
        e.initTouchEvent(type, true, true,
                         null, null, false, false, false, false,
                         touches, null, changed);
  
        return e;
      }
  
      function forgeMouseEvent(type, x, y) {
        var e = document.createEvent('MouseEvent');
  
        e.initMouseEvent(type, true, true, window, 1, x, y, x, y,
                         false, false, false, false, 0, null);
  
        return e;
      }
  
      function fakeDispatch(type, x, y) {
        var e;
        if (type.startsWith('mouse')) {
          e = forgeMouseEvent(type, x, y);
        } else {
          e = forgeTouchEvent(type, x, y);
        }
        subject.handleStatusbarTouch(e, 24);
  
        return e;
      }
  
      suite('Revealing the StatusBar >', function() {
        var transitionEndSpy;
        var element;
        setup(function() {
          element = subject.titleBar;
          transitionEndSpy = this.sinon.spy(element, 'addEventListener');
        });
  
        function assertStatusBarReleased() {
          assert.isFalse(element.classList.contains('dragged'));
        }
  
        function assertChromeBarDragging(isDragging) {
          assert.equal(subject.chromeBar.classList.contains('dragging'),
            isDragging);
        }
  
        teardown(function() {
          this.sinon.clock.tick(10000);
        });
  
        test('it should prevent default on mouse events',
          function() {
            var mousedown = fakeDispatch('mousedown', 100, 0);
            var mousemove = fakeDispatch('mousemove', 100, 2);
            var mouseup = fakeDispatch('mouseup', 100, 2);
  
            assert.isTrue(mousedown.defaultPrevented);
            assert.isTrue(mousemove.defaultPrevented);
            assert.isTrue(mouseup.defaultPrevented);
          });
  
        test('it should prevent default on all touch events to prevent reflows',
          function() {
            var touchstart = fakeDispatch('touchstart', 100, 0);
            var touchmove = fakeDispatch('touchmove', 100, 2);
            var touchend = fakeDispatch('touchend', 100, 2);
  
            assert.isTrue(touchstart.defaultPrevented);
            assert.isTrue(touchmove.defaultPrevented);
            assert.isTrue(touchend.defaultPrevented);
          });
  
        test('chromeBar dragging, then dragged then released when above ' +
          'threshold', function() {
          assertChromeBarDragging(false);
          fakeDispatch('touchstart', 100, 0);
          assertChromeBarDragging(true);
          fakeDispatch('touchmove', 100, 24);
          assertChromeBarDragging(true);
          fakeDispatch('touchend', 100, 25);
          assertChromeBarDragging(false);
          assert.isTrue(element.classList.contains('dragged'));
        });
  
        test('chromeBar dragging then released when below threshold', function() {
          assertChromeBarDragging(false);
          fakeDispatch('touchstart', 100, 0);
          assertChromeBarDragging(true);
          fakeDispatch('touchmove', 100, 2);
          assertChromeBarDragging(true);
          fakeDispatch('touchend', 100, 2);
          assertChromeBarDragging(false);
          assertStatusBarReleased();
        });
  
        test('it should stop the propagation of the events at first', function() {
          var fakeEvt = {
            stopImmediatePropagation: function() {},
            preventDefault: function() {},
            type: 'fake'
          };
          this.sinon.spy(fakeEvt, 'stopImmediatePropagation');
          subject.handleStatusbarTouch(fakeEvt, 24);
          sinon.assert.calledOnce(fakeEvt.stopImmediatePropagation);
        });
  
        test('it should translate the statusbar on touchmove', function() {
          fakeDispatch('touchstart', 100, 0);
          fakeDispatch('touchmove', 100, 5);
          var transform = 'translateY(calc(5px - 100%))';
          assert.equal(element.style.transform, transform);
          fakeDispatch('touchend', 100, 5);
        });
  
        test('it should set the dragged class on touchstart', function() {
          fakeDispatch('touchstart', 100, 0);
          assert.isFalse(element.classList.contains('dragged'));
          fakeDispatch('touchmove', 100, 24);
          fakeDispatch('touchend', 100, 25);
          assert.isTrue(element.classList.contains('dragged'));
        });
  
        test('it should not translate the statusbar more than its height',
        function() {
          fakeDispatch('touchstart', 100, 0);
          fakeDispatch('touchmove', 100, 5);
          fakeDispatch('touchmove', 100, 15);
          var transform = 'translateY(calc(15px - 100%))';
          assert.equal(element.style.transform, transform);
          fakeDispatch('touchend', 100, 15);
        });
  
        suite('after the gesture', function() {
          suite('when the StatusBar is not fully displayed', function() {
            setup(function() {
              fakeDispatch('touchstart', 100, 0);
              fakeDispatch('touchmove', 100, 5);
              fakeDispatch('touchend', 100, 5);
            });
  
            test('it should hide it right away', function() {
              assertStatusBarReleased();
            });
          });
  
          suite('when the StatusBar is fully displayed', function() {
            setup(function() {
              fakeDispatch('touchstart', 100, 0);
              fakeDispatch('touchmove', 100, 5);
              fakeDispatch('touchmove', 100, 24);
              fakeDispatch('touchend', 100, 24);
            });
  
            test('it should not hide it right away', function() {
              assert.equal(subject.titleBar.style.transform, '');
              assert.equal(subject.titleBar.style.transition, '');
              assert.ok(subject.titleBar.classList.contains('dragged'));
            });
  
            test('but after 5 seconds', function() {
              this.sinon.clock.tick(5000);
              assertStatusBarReleased();
            });
  
            test('or if the utility tray hides', function() {
              window.dispatchEvent(new CustomEvent('utilitytraywillshow'));
              assertStatusBarReleased();
            });
  
            test('or if the user interacts with the app', function() {
              // We're faking a touchstart event on the app iframe
              var iframe = app.browser.element;
              subject._touchForwarder.destination = window;
  
              var e = forgeTouchEvent('touchstart', 100, 100);
              window.dispatchEvent(e);
  
              assertStatusBarReleased();
              subject._touchForwarder.destination = iframe;
            });
          });
        });
      });
  
      suite('Touch forwarding in fullscreen >', function() {
        var forwardSpy;
  
        setup(function() {
          forwardSpy = this.sinon.spy(MockTouchForwarder.prototype, 'forward');
        });
  
        test('it should forward taps to the app', function() {
          var touchstart = fakeDispatch('touchstart', 100, 0);
          fakeDispatch('touchmove', 100, 2);
          var touchend = fakeDispatch('touchend', 100, 2);
  
          assert.isTrue(forwardSpy.calledTwice);
          var call = forwardSpy.firstCall;
          assert.equal(call.args[0], touchstart);
          call = forwardSpy.getCall(1);
          assert.equal(call.args[0], touchend);
        });
  
        suite('if it\'s not a tap and the statusbar is not fully displayed',
        function() {
          test('it should not forward any events', function() {
            fakeDispatch('touchstart', 100, 0);
            fakeDispatch('touchmove', 100, 8);
            fakeDispatch('touchend', 100, 8);
  
            assert.isTrue(forwardSpy.notCalled);
          });
        });
  
        test('it should forward touchmove once the statusbar is shown',
        function() {
          var touchstart = fakeDispatch('touchstart', 100, 0);
          fakeDispatch('touchmove', 100, 6);
          var secondMove = fakeDispatch('touchmove', 100, 26);
          var thirdMove = fakeDispatch('touchmove', 100, 28);
          var touchend = fakeDispatch('touchend', 100, 2);
  
          assert.equal(forwardSpy.callCount, 4);
          var call = forwardSpy.firstCall;
          assert.equal(call.args[0], touchstart);
          call = forwardSpy.getCall(1);
          assert.equal(call.args[0], secondMove);
          call = forwardSpy.getCall(2);
          assert.equal(call.args[0], thirdMove);
          call = forwardSpy.getCall(3);
          assert.equal(call.args[0], touchend);
        });
      });
    });
  });
  