describe('$anchorScroll', function() {

  var elmSpy;

  function addElements() {
    var elements = sliceArgs(arguments);

    return function() {
      forEach(elements, function(identifier) {
        var match = identifier.match(/(\w* )?(\w*)=(\w*)/),
            jqElm = jqLite('<' + (match[1] || 'a ') + match[2] + '="' + match[3] + '"/>'),
            elm = jqElm[0];

        elmSpy[identifier] = spyOn(elm, 'scrollIntoView');
        jqLite(document.body).append(jqElm);
      });
    };
  }

  function changeHashAndScroll(hash) {
    return function($location, $anchorScroll) {
      $location.hash(hash);
      $anchorScroll();
    };
  }

  function expectScrollingToTop($window) {
    forEach(elmSpy, function(spy, id) {
      expect(spy).not.toHaveBeenCalled();
    });

    expect($window.scrollTo).toHaveBeenCalledWith(0, 0);
  }

  function expectScrollingTo(identifier) {
    return function($window) {
      forEach(elmSpy, function(spy, id) {
        if (identifier === id) expect(spy).toHaveBeenCalledOnce();
        else expect(spy).not.toHaveBeenCalled();
      });
      expect($window.scrollTo).not.toHaveBeenCalled();
    };
  }

  function expectNoScrolling() {
    return expectScrollingTo(NaN);
  }


  beforeEach(module(function($provide) {
    elmSpy = {};
    $provide.value('$window', {
      scrollTo: jasmine.createSpy('$window.scrollTo'),
      document: document,
      navigator: {}
    });
  }));


  it('should scroll to top of the window if empty hash', inject(
    changeHashAndScroll(''),
    expectScrollingToTop));


  it('should not scroll if hash does not match any element', inject(
    addElements('id=one', 'id=two'),
    changeHashAndScroll('non-existing'),
    expectNoScrolling()));


  it('should scroll to anchor element with name', inject(
    addElements('a name=abc'),
    changeHashAndScroll('abc'),
    expectScrollingTo('a name=abc')));


  it('should not scroll to other than anchor element with name', inject(
    addElements('input name=xxl', 'select name=xxl', 'form name=xxl'),
    changeHashAndScroll('xxl'),
    expectNoScrolling()));


  it('should scroll to anchor even if other element with given name exist', inject(
    addElements('input name=some', 'a name=some'),
    changeHashAndScroll('some'),
    expectScrollingTo('a name=some')));


  it('should scroll to element with id with precedence over name', inject(
    addElements('name=abc', 'id=abc'),
    changeHashAndScroll('abc'),
    expectScrollingTo('id=abc')));


  it('should scroll to top if hash == "top" and no matching element', inject(
    changeHashAndScroll('top'),
    expectScrollingToTop));


  it('should scroll to element with id "top" if present', inject(
    addElements('id=top'),
    changeHashAndScroll('top'),
    expectScrollingTo('id=top')));


  describe('watcher', function() {

    function initLocation(config) {
      return function($provide, $locationProvider) {
        $provide.value('$sniffer', {history: config.historyApi});
        $locationProvider.html5Mode(config.html5Mode);
      };
    }

    function changeHashTo(hash) {
      return function ($location, $rootScope, $anchorScroll) {
        $rootScope.$apply(function() {
          $location.hash(hash);
        });
      };
    }

    function disableAutoScrolling() {
      return function($anchorScrollProvider) {
        $anchorScrollProvider.disableAutoScrolling();
      };
    }

    afterEach(inject(function($document) {
      dealoc($document);
    }));


    it('should scroll to element when hash change in hashbang mode', function() {
      module(initLocation({html5Mode: false, historyApi: true}));
      inject(
        addElements('id=some'),
        changeHashTo('some'),
        expectScrollingTo('id=some')
      );
    });


    it('should scroll to element when hash change in html5 mode with no history api', function() {
      module(initLocation({html5Mode: true, historyApi: false}));
      inject(
        addElements('id=some'),
        changeHashTo('some'),
        expectScrollingTo('id=some')
      );
    });


    it('should not scroll when element does not exist', function() {
      module(initLocation({html5Mode: false, historyApi: false}));
      inject(
        addElements('id=some'),
        changeHashTo('other'),
        expectNoScrolling()
      );
    });


    it('should scroll when html5 mode with history api', function() {
      module(initLocation({html5Mode: true, historyApi: true}));
      inject(
        addElements('id=some'),
        changeHashTo('some'),
        expectScrollingTo('id=some')
      );
    });


    it('should not scroll when disabled', function() {
      module(
          disableAutoScrolling(),
          initLocation({html5Mode: false, historyApi: false})
      );
      inject(
        addElements('id=fake'),
        changeHashTo('fake'),
        expectNoScrolling()
      );
    });
  });
});