require 'spec/spec_helper'
require 'taza/page'
require 'taza/site'

describe Taza::Page do
  before :each do
    @page_class = Class.new(Taza::Page)
  end
  
  class ElementAndFilterContextExample < Taza::Page
    element(:sample_element) {browser}
    filter:sample_filter, :sample_element
    def sample_filter
      browser
    end
  end

  class RecursiveFilterExample < Taza::Page
    element(:foo) {}
    filter :sample_filter
    def sample_filter
      foo
      true
    end
  end

  it "should not enter a infinite loop if you call a filtered element inside of a filter" do
    page = RecursiveFilterExample.new
    lambda { page.foo }.should_not raise_error
  end

  it "should execute an element's block with the params provided for its method" do
    @page_class.element(:boo){|baz| baz}
    @page_class.new.boo("rofl").should == "rofl"
  end
  
  it "should execute elements and filters in the context of the page instance" do
    page = ElementAndFilterContextExample.new
    page.browser = :something
    page.sample_element.should eql(:something)
  end
  
  it "should add a filter to the classes filters" do
    ElementAndFilterContextExample.filters.size.should eql(1) 
  end
  
  it "should store the block given to the element method in a method with the name of the parameter" do
    @page_class.element(:foo) do
      "bar"
    end
    @page_class.new.foo.should == "bar"
  end

  class FilterAllElements < Taza::Page
    element(:foo) {}
    element(:apple) {}
    filter :false_filter

    def false_filter
      false
    end
  end
    
  it "should filter all elements if element argument is not provided" do
    lambda { FilterAllElements.new.apple }.should raise_error(Taza::FilterError)
    lambda { FilterAllElements.new.foo }.should raise_error(Taza::FilterError)
  end
  
  it "should have empty elements on a new class" do
    foo = @page_class
    foo.elements.should_not be_nil
    foo.elements.should be_empty
  end

  it "should have empty filters on a new class" do
    foo = @page_class
    foo.filters.should_not be_nil
    foo.filters.should be_empty
  end

  class FilterAnElement < Taza::Page
    attr_accessor :called_element_method
    element(:false_item) { @called_element_method = true}
    filter :false_filter, :false_item

    def false_filter
      false
    end
  end

  it "should raise a error if an elements is called and its filter returns false" do
    lambda { FilterAnElement.new.false_item }.should raise_error(Taza::FilterError)
  end
  
  it "should not call element block if filters fail" do
    page = FilterAnElement.new
    lambda { page.false_item }.should raise_error
    page.called_element_method.should_not be_true
  end

  class CheckOutPage < Taza::Page
    url 'check_out'
  end
  
  it "should goto the url relative to the site url" do
    browser = stub
    browser.expects(:goto).with('http://www.llamas.com')
    browser.expects(:goto).with('http://www.llamas.com/check_out')
    Taza::Settings.stubs(:config).returns(:url => 'http://www.llamas.com')    
    page = CheckOutPage.new
    page.site = Class.new(Taza::Site).new(:browser => browser)
    page.goto
  end  

  it "should report the full url of the page" do
    browser = stub
    browser.stubs(:goto)
    Taza::Settings.stubs(:config).returns(:url => 'http://www.llamas.com')    
    page = CheckOutPage.new
    page.site = Class.new(Taza::Site).new(:browser => browser)
    page.full_url.should == 'http://www.llamas.com/check_out'
    page.url.should == page.full_url
  end
    
  it "should create elements for fields" do    
    @page_class.field(:foo) {'element'}
    @page_class.new.foo_field.should == 'element'
  end
  
  it "should allow you to override the suffix for fields" do
    @page_class.field(:foo, 'link') {'link element'}
    @page_class.new.foo_link.should == 'link element'
  end
  
  it "should return the display_value of the field's element" do
    element = stub
    element.stubs(:display_value).with().returns('tomorrow')
    @page_class.field(:foo) {element}
    page = @page_class.new
    page.foo_field.display_value.should == 'tomorrow'
    page.foo.should == 'tomorrow'
  end
  
  it "should call the set method of the element when the field= method is called" do
    element = stub
    element.stubs(:set).with('never').returns(nil)
    @page_class.field(:end_date) {element}
    page = @page_class.new
    page.end_date = 'never'
  end
  
  def uses_soldier_page
    name_element = stub
    rank_element = stub
    serial_no_element = stub
    @page_class.class_eval do
      field(:name){name_element}
      field(:rank){rank_element}
      field(:serial_no){serial_no_element}
    end
    @soldier_page = @page_class.new
    @soldier_page.name_field.stubs(:display_value).returns('Zachary Taylor')
    @soldier_page.rank_field.stubs(:display_value).returns('General')
    @soldier_page.serial_no_field.stubs(:display_value).returns('unknown')
  end
  
  it "should populate the fields corresponding to the keys in the hash" do
    uses_soldier_page
    @soldier_page.name_field.expects(:set).with('Zachary Taylor')
    @soldier_page.rank_field.expects(:set).with('General')
    @soldier_page.populate :name => 'Zachary Taylor', :rank => 'General'
  end

  it "should validate a page" do
    uses_soldier_page
    @soldier_page.validate :name => 'Zachary Taylor', :rank => 'General'
  end

  it "should allow you to define elements using human-form names" do
    @page_class.element('User Name'){'Tertulian'}
    @page_class.new.user_name.should == 'Tertulian'
  end
  
  it "should allow you to define fields using human-form names" do
    @page_class.field('User Name'){'Tertulian'}
    @page_class.new.user_name_field.should == 'Tertulian'
  end  

  def simple_page
    @page_class.class_eval do
      element(:link){}
      field(:name){}
    end
    @page_class.new
  end

  it "should list the page's elements" do
    simple_page.elements.should == ['link', 'name_field']
  end

  it "should list the page's fields" do
    simple_page.fields.should == ['name']
  end
  
  it "should list the values of all the fields" do
    uses_soldier_page
    @soldier_page.values.should == 
      {:name => 'Zachary Taylor', :rank => 'General', :serial_no => 'unknown'}
  end
  
  it "should list the values of the specified fields" do
    uses_soldier_page
    @soldier_page.values(['name', 'rank']).should ==
      {:name => 'Zachary Taylor', :rank => 'General'}
  end
  
  def exists_page
    link1_element = stub
    link1_element.stubs(:exist?).returns(true)
    link2_element = stub
    link2_element.stubs(:exist?).returns(false)
    @page_class.class_eval do
      element(:link1){link1_element}
      element(:link2){link2_element}
    end
    @page_class.new    
  end
  
  it "should determine whether an element exists" do
    page = exists_page
    page.element_exists?(:link1).should be_true
    page.element_exist?(:link1).should be_true
    page.element_exists?(:link2).should be_false
  end

  it "should report which defined elements exist" do
    page = exists_page
    expectation = {:link1 => true, :link2 => false}
    page.elements_exist?.should == expectation
    page.elements_exists?.should == expectation
  end
  
  it "should report whether selected elements exist" do
    page = exists_page
    expectation = {:link1 => true}
    page.elements_exist?([:link1]).should == expectation
  end
  
  it "should report that elements don't exist when not found errors are thrown" do
    link3_element = stub
    link3_element.stubs(:exist?).raises(Watir::Exception::UnknownFrameException)
    link4_element = stub
    link4_element.stubs(:exist?).raises(Watir::Exception::UnknownObjectException)
    @page_class.class_eval do
      element(:link3){link3_element}
      element(:link4){link4_element}
    end
    page = @page_class.new    
    page.element_exists?(:link3).should == false
    page.element_exists?(:link4).should == false
  end
  
  describe "tables" do
    
    it "you should be able to create and reference" do
      @page_class.class_eval do
        element(:results_table){}
        table(:results) {}
      end
      @page_class.new.results.should be_a(WatirCraft::Table)
    end
    
    require 'spec/fake_table'
    it "should report whether a table has a specified row" do
      @page_class.class_eval do
        element(:results_table){FakeTable.new [:name => 'x']}
        table(:results) do
          field(:name){@row.element(:name)}
        end
      end
      
      @page_class.new.results.row(:name => 'x').should_not be_nil
    end
    
    def uses_table_page
      fake_table = FakeTable.new [
          {:letter => 'x', :number => 1}, 
          {:letter => 'y', :number => 2}
          ]
      @page_class.class_eval do
        element(:results_table) {fake_table}
        table(:results) do
          field(:name){row.element(:letter)}
          field(:phone){row.element(:number)}
          field(:missing){raise 'not found'}
        end
      end
      @table_page = @page_class.new
      @fake_table = fake_table
    end
    
    it "should allow you to reference another field in a selected row" do
      uses_table_page
      @table_page.results.row(:name => 'x').phone.should == 1
      @table_page.results.row(:phone => 2).name.should == 'y'
    end
    
    it "fields should have underlying elements" do
      uses_table_page
      @table_page.results.row(:name => 'x').phone_field.should exist
    end
    
    it "should have elements" do
      @page_class.class_eval do
        element(:results_table) do FakeTable.new [
          {:letter => 'x', :number => 1}, 
          {:letter => 'y', :number => 2}
          ]
        end
        table(:results) do
          field(:name){row.element(:letter)}
          element(:phone){row.element(:number)}
        end
      end
      @table_page = @page_class.new
      @table_page.results.row(:name => 'x').phone.should be_a(FakeElement)
    end
    
    it "should allow you to set a field in a row" do
      uses_table_page
      @table_page.results.row(:name => 'x').phone = '22'
      @fake_table.rows[0].element(:number).display_value.should == '22'
    end

    it "rows should exist" do
      uses_table_page
      @table_page.results.row(:name => 'x').should exist
    end
        
    it "should not raise an exception if a field isn't found" do
      uses_table_page
      @table_page.results.row(:missing => 'nada').should be_nil
    end
    
    it "should be able to be listed from the page" do
      uses_table_page
      @table_page.tables.should == ['results']
    end
    
    it "should list its fields" do
      uses_table_page
      @table_page.results.fields.should == %w(name phone missing)
    end
    
    it "should list its elements" do
      @page_class.class_eval do
        element(:results_table) do FakeTable.new []
        end
        table(:results) do
          field(:name){row.element(:letter)}
          element(:phone){row.element(:number)}
        end
      end
      @table_page = @page_class.new
      @table_page.results.elements.should == %w(name_field phone)
    end
  
  end
  
end
