'use strict';

describe('$sniffer', function() {

  function sniffer($window, $document) {
    /* global $SnifferProvider: false */
    $window.navigator = {};
    $document = jqLite($document || {});
    if (!$document[0].body) {
      $document[0].body = window.document.body;
    }
    return new $SnifferProvider().$get[2]($window, $document);
  }

  describe('history', function() {
    it('should be true if history.pushState defined', function() {
      expect(sniffer({history: {pushState: noop, replaceState: noop}}).history).toBe(true);
    });

    it('should be false if history or pushState not defined', function() {
      expect(sniffer({history: {}}).history).toBe(false);
      expect(sniffer({}).history).toBe(false);
    });
  });


  describe('hasEvent', function() {
    var mockDocument, mockDivElement, $sniffer;

    beforeEach(function() {
      mockDocument = {createElement: jasmine.createSpy('createElement')};
      mockDocument.createElement.andCallFake(function(elm) {
        if (elm === 'div') return mockDivElement;
      });

      $sniffer = sniffer({}, mockDocument);
    });


    it('should return true if "onchange" is present in a div element', function() {
      mockDivElement = {onchange: noop};

      expect($sniffer.hasEvent('change')).toBe(true);
    });


    it('should return false if "oninput" is not present in a div element', function() {
      mockDivElement = {};

      expect($sniffer.hasEvent('input')).toBe(false);
    });


    it('should only create the element once', function() {
      mockDivElement = {};

      $sniffer.hasEvent('change');
      $sniffer.hasEvent('change');
      $sniffer.hasEvent('change');

      expect(mockDocument.createElement).toHaveBeenCalledOnce();
    });


    it('should claim that IE9 doesn\'t have support for "oninput"', function() {
      // IE9 implementation is fubared, so it's better to pretend that it doesn't have the support
      // IE10+ implementation is fubared when mixed with placeholders
      mockDivElement = {oninput: noop};

      expect($sniffer.hasEvent('input')).toBe(!(msie && msie <= 11));
    });
  });


  describe('csp', function() {
    it('should be false by default', function() {
      expect(sniffer({}).csp).toBe(false);
    });
  });


  describe('vendorPrefix', function() {

    it('should return the correct vendor prefix based on the browser', function() {
      inject(function($sniffer, $window) {
        var expectedPrefix;
        var ua = $window.navigator.userAgent.toLowerCase();
        if (/chrome/i.test(ua) || /safari/i.test(ua) || /webkit/i.test(ua)) {
          expectedPrefix = 'Webkit';
        }
        else if (/firefox/i.test(ua)) {
          expectedPrefix = 'Moz';
        }
        else if (/ie/i.test(ua) || /trident/i.test(ua)) {
          expectedPrefix = 'Ms';
        }
        expect($sniffer.vendorPrefix).toBe(expectedPrefix);
      });
    });

    it('should still work for an older version of Webkit', function() {
      module(function($provide) {
        var doc = {
          body: {
            style: {
              WebkitOpacity: '0'
            }
          }
        };
        $provide.value('$document', jqLite(doc));
      });
      inject(function($sniffer) {
        expect($sniffer.vendorPrefix).toBe('webkit');
      });
    });

  });

  describe('animations', function() {
    it('should be either true or false', function() {
      inject(function($sniffer) {
        expect($sniffer.animations).not.toBe(undefined);
      });
    });

    it('should be false when there is no animation style', function() {
      module(function($provide) {
        var doc = {
          body: {
            style: {}
          }
        };
        $provide.value('$document', jqLite(doc));
      });
      inject(function($sniffer) {
        expect($sniffer.animations).toBe(false);
      });
    });

    it('should be true with vendor-specific animations', function() {
      module(function($provide) {
        var animationStyle = 'some_animation 2s linear';
        var doc = {
          body: {
            style: {
              WebkitAnimation: animationStyle,
              MozAnimation: animationStyle
            }
          }
        };
        $provide.value('$document', jqLite(doc));
      });
      inject(function($sniffer) {
        expect($sniffer.animations).toBe(true);
      });
    });

    it('should be true with w3c-style animations', function() {
      module(function($provide) {
        var doc = {
          body: {
            style: {
              animation: 'some_animation 2s linear'
            }
          }
        };
        $provide.value('$document', jqLite(doc));
      });
      inject(function($sniffer) {
        expect($sniffer.animations).toBe(true);
      });
    });

    it('should be true on android with older body style properties', function() {
      module(function($provide) {
        var doc = {
          body: {
            style: {
              webkitAnimation: ''
            }
          }
        };
        var win = {
          navigator: {
            userAgent: 'android 2'
          }
        };
        $provide.value('$document', jqLite(doc));
        $provide.value('$window', win);
      });
      inject(function($sniffer) {
        expect($sniffer.animations).toBe(true);
      });
    });

    it('should be true when an older version of Webkit is used', function() {
      module(function($provide) {
        var doc = {
          body: {
            style: {
              WebkitOpacity: '0'
            }
          }
        };
        $provide.value('$document', jqLite(doc));
      });
      inject(function($sniffer) {
        expect($sniffer.animations).toBe(false);
      });
    });

  });

  describe('transitions', function() {

    it('should be either true or false', function() {
      inject(function($sniffer) {
        expect($sniffer.transitions).not.toBe(undefined);
      });
    });

    it('should be false when there is no transition style', function() {
      module(function($provide) {
        var doc = {
          body: {
            style: {}
          }
        };
        $provide.value('$document', jqLite(doc));
      });
      inject(function($sniffer) {
        expect($sniffer.transitions).toBe(false);
      });
    });

    it('should be true with vendor-specific transitions', function() {
      module(function($provide) {
        var transitionStyle = '1s linear all';
        var doc = {
          body: {
            style: {
              WebkitTransition: transitionStyle,
              MozTransition: transitionStyle
            }
          }
        };
        $provide.value('$document', jqLite(doc));
      });
      inject(function($sniffer) {
        expect($sniffer.transitions).toBe(true);
      });
    });

    it('should be true with w3c-style transitions', function() {
      module(function($provide) {
        var doc = {
          body: {
            style: {
              transition: '1s linear all'
            }
          }
        };
        $provide.value('$document', jqLite(doc));
      });
      inject(function($sniffer) {
        expect($sniffer.transitions).toBe(true);
      });
    });

    it('should be true on android with older body style properties', function() {
      module(function($provide) {
        var doc = {
          body: {
            style: {
              webkitTransition: ''
            }
          }
        };
        var win = {
          navigator: {
            userAgent: 'android 2'
          }
        };
        $provide.value('$document', jqLite(doc));
        $provide.value('$window', win);
      });
      inject(function($sniffer) {
        expect($sniffer.transitions).toBe(true);
      });
    });

  });


  describe('history', function() {
    it('should be true on Boxee box with an older version of Webkit', function() {
      module(function($provide) {
        var doc = {
          body: {
            style: {}
          }
        };
        var win = {
          history: {
            pushState: noop
          },
          navigator: {
            userAgent: 'boxee (alpha/Darwin 8.7.1 i386 - 0.9.11.5591)'
          }
        };
        $provide.value('$document', jqLite(doc));
        $provide.value('$window', win);
      });
      inject(function($sniffer) {
        expect($sniffer.history).toBe(false);
      });
    });
  });

  it('should provide the android version', function() {
    module(function($provide) {
      var win = {
        navigator: {
          userAgent: 'android 2'
        }
      };
      $provide.value('$document', jqLite({}));
      $provide.value('$window', win);
    });
    inject(function($sniffer) {
      expect($sniffer.android).toBe(2);
    });
  });
});
