$ = jQuery

describe "Superfish", ->

  $menu = null
  $li_with_sub = null

  beforeEach ->
    $menu = $('ul.sf-menu')
    $li_with_sub = $menu.find('li:has(ul):first')
    $menu.superfish()
  
  afterEach ->
    $menu.superfish('destroy')

  it "should exist", ->
    expect($.fn.superfish).toBeDefined()

  it "should be chainable", ->
    $menu.superfish('destroy')
    expect($menu.superfish()).toBe 'ul'

  it "should store options", ->
    expect($menu.data('sf-options') ).toBeDefined()


  describe "options", ->

    it "should have default options", ->
      options = $menu.data('sf-options')
      expect(options.speed).toMatch 'normal'

    it "should allow default options to be overridden", ->
      $menu.superfish('destroy')
      $menu.superfish
        speed: 1000
      options = $menu.data('sf-options')
      expect(options.speed).toEqual 1000


  describe "method access", ->

    it "'show' method should exist", ->
      expect($li_with_sub.superfish('show')).toBeDefined()

    it "'hide' method should exist", ->
      expect($li_with_sub.superfish('hide')).toBeDefined()

    it "'destroy' method should exist", ->
      expect($menu.superfish('destroy')).toBeDefined()
      $menu.superfish()

    it "should not allow access to private functions", ->
      expect( ->
        $menu.superfish('close')
      ).toThrow new Error('Method close does not exist on jQuery.fn.superfish')

    it "should not throw an error when accessing a valid method", ->
      expect( ->
        $li_with_sub.superfish('show')
      ).not.toThrow new Error('Method show does not exist on jQuery.fn.superfish')


  describe "'destroy' method", ->

    it "should handle multiple calls gracefully", ->
      $menu.superfish('destroy')
      expect( ->
        $menu.superfish('destroy')
      ).not.toThrow new Error("Uncaught TypeError: Cannot read property 'sfTimer' of null")
      expect($menu.superfish('destroy')).toBe 'ul'


  describe "'show' method", ->
    it "should fail silently if Superfish is uninitialised", ->
      $menu.superfish('destroy')
      expect( $li_with_sub.superfish('show') ).toBe 'li'

    it "should cause child ul to be visible", ->
      $submenu = $li_with_sub.children('ul')
      expect($submenu).toBeHidden()
      expect($li_with_sub).not.toBe '.sfHover'
      $li_with_sub.superfish('show')
      expect($submenu).toBeVisible()
      expect($li_with_sub).toBe '.sfHover'


  describe "'hide' method", ->
    it "should fail silently if Superfish is uninitialised", ->
      $menu.superfish('destroy')
      expect( $li_with_sub.superfish('hide') ).toBe 'li'
      
    it "should cause child ul to be hidden", ->
      $submenu = $li_with_sub.children('ul')
      $li_with_sub.superfish('show')
      expect($li_with_sub).toBe '.sfHover'
      expect($submenu).toBeVisible()
      # do an instant hide for now until I figure out why .toBeHidden fails when animated
      $li_with_sub.superfish('hide', true)
      expect($submenu).toBeHidden()
      expect($li_with_sub).not.toBe '.sfHover'


  describe "initialisation", ->
    it "should fail silently if already initialised", ->
      $menu.superfish('destroy')
      init_count = 0
      $.fn.superfish.defaults.onInit = -> init_count++
      $menu.superfish().superfish()
      expect(init_count).toEqual 1
      $.fn.superfish.defaults.onInit = $.noop

    it "should be able to store the path to the 'current' menu item (pathClass)", ->
      expect($menu.data('sf-options').$path.length).toEqual 0
      $menu.superfish('destroy')
      $menu.superfish
        pathClass: 'current'
      expect($menu.data('sf-options').$path.length).toEqual 1


  describe "pathClass feature", ->
    it "should show 'current' submenu", ->
      $menu.superfish('destroy')
      $menu.superfish
        pathClass: 'current'
      expect($li_with_sub).toBe '.sfHover'


  describe "callbacks", ->

    describe "onDestroy", ->
      it "should fire", ->
        destroy_count = 0
        $menu.superfish('destroy')
        $menu.superfish
          onDestroy: -> destroy_count++
        $menu.superfish('destroy')
        expect(destroy_count).toEqual 1
