require 'test/unit'

module Borges; end
require 'Borges/Extensions/Array'
require 'Borges/Extensions/Numeric'
require 'Borges/Extensions/Object'
require 'Borges/Extensions/Proc'
require 'Borges/Extensions/String'

require 'Borges/HTML/HtmlAttributes'
require 'Borges/HTML/HtmlElement'
require 'Borges/HTML/HtmlBuilder'
require 'Borges/HTML/HtmlRenderer'
require 'Borges/HTML/RenderingContext'
require 'Borges/TestCase/Util/HtmlDocument'
require 'Borges/TestCase/Util/CallbackStore'

class HtmlRendererTest < Test::Unit::TestCase

  def setup
    @url = "http://example.com/"
  end

  def render
    str = ''
    rc = Borges::RenderingContext.new(HtmlDocument.new,
                                      @url, CallbackStore.new)
    yield ::Borges::HtmlRenderer.new(rc)
    rc.document.print_html_on str
    return str
  end

  def assert_renders(expected, message = nil, &block)
    assert_equal "<html><head></head><body>#{expected}</body></html>",
                 render(&block), message
  end

  def test_anchor
    assert_renders("<a href=\"#{@url}?1\">text</a>") do |r|
      r.anchor("text") do end
    end
  end

  def test_anchor_on
    assert_renders("<a id=\"text\" href=\"#{@url}?1\">Text</a>") do |r|
      r.anchor_on :text, Object.new
    end
  end

  def test_boolean_menu_default_true
    expected = "<select name=\"1\"><option value=\"2\" selected=\"selected\">Yes</option><option value=\"3\">No</option></select>"
    output = render do |r|
      r.boolean_menu(true) do end
    end

    assert_match(/<select/, output, "Has a select element")
    assert_match(/<option.*<option/, output, "Has two option elements")
    assert_match(/"><[^>]+selected="selected"/, output,
                 "First option is selected")
    assert_match(/"><[^>]+>Yes</, output, "First option is \"Yes\"")
    assert_match(/n><option[^>]+>No</, output, "Second option is \"No\"")
  end

  def test_boolean_menu_default_false
    output = render do |r|
      r.boolean_menu(false) do end
    end

    assert_match(/<select/, output, "Has a select element")
    assert_match(/<option.*<option/, output, "Has two option elements")
    assert_match(/n><[^>]+selected="selected"/, output,
                 "Second option is selected")
    assert_match(/"><[^>]+>Yes</, output, "First option is \"Yes\"")
    assert_match(/n><option[^>]+>No</, output, "Second option is \"No\"")
  end

  def test_boolean_menu_with_labels
    expected = "<select name=\"1\"><option value=\"2\" selected=\"selected\">Ok</option><option value=\"3\">Cancel</option></select>"
    output = render do |r|
      r.boolean_menu(true, [:Ok, :Cancel]) do end
    end

    assert_match(/<select/, output, "Has a select element")
    assert_match(/<option.*<option/, output, "Has two option elements")
    assert_match(/"><[^>]+selected="selected"/, output,
                 "First option is selected")
    assert_match(/"><[^>]+>Ok</, output, "First option is \"Ok\"")
    assert_match(/n><option[^>]+>Cancel</, output, "Second option is \"Cancel\"")
  end

  def test_boolean_menu_on
    obj = Object.new
    obj.instance_eval "def foo() true end"

    expected = "<select id=\"foo\" name=\"1\"><option value=\"2\" selected=\"selected\">Yes</option><option value=\"3\">No</option></select>"
    output = render do |r|
      r.boolean_menu_on :foo, obj
    end

    assert_match(/<select/, output, "Has a select element")
    assert_match(/<select[^>]+id="foo"/, output, "Select has id of foo")
    assert_match(/<option.*<option/, output, "Has two option elements")
    assert_match(/"><[^>]+selected="selected"/, output,
                 "First option is selected")
    assert_match(/"><[^>]+>Yes</, output, "First option is \"Yes\"")
    assert_match(/n><option[^>]+>No</, output, "Second option is \"No\"")
  end

  def test_setter_callback
    obj = Object.new
    obj.instance_eval "def foo=(val) @val = val end"

    rendering_context = Borges::RenderingContext.new(HtmlDocument.new,
                                                     @url,
                                                     CallbackStore.new)
    r = ::Borges::HtmlRenderer.new(rendering_context)

    r.setter_callback(:foo, obj).call "value"

    assert_equal "value", obj.instance_variable_get("@val")
  end

  def test_action_callback
    obj = Object.new
    obj.instance_eval "def foo() @val = true end"

    rendering_context = Borges::RenderingContext.new(HtmlDocument.new,
                                                     @url,
                                                     CallbackStore.new)
    r = ::Borges::HtmlRenderer.new(rendering_context)

    r.action_callback(:foo, obj).call

    assert obj.instance_variable_get("@val")
  end

  def test_checkbox_checked
    expected = "<input checked=\"checked\" type=\"checkbox\" value=\"true\" name=\"1\"><input type=\"hidden\" value=\"false\" name=\"1\">"
    output = render do |r|
      r.checkbox(true) do end
    end

    # ... <body><input ...
    assert_match(/y><input/, output, "Is an input element")
    assert_match(/y><[^>]+type="checkbox"/, output, "Is a checkbox")
    assert_match(/y><[^>]+value="true"/, output, "Is set to true")
    assert_match(/y><[^>]+name="1"/, output, "Has a name")

    # ... <input ...><input ...
    assert_match(/"><input/, output, "Has a second hidden input")
    assert_match(/"><[^>]+type="hidden"/, output, "Second input is hidden")
    assert_match(/"><[^>]+value="false"/, output,
                 "Second input is set to false")
    assert_match(/"><[^>]+name="1"/, output,
                 "Second input has a name with the same value as the first")
  end

  def test_checkbox
    output = render do |r|
      r.checkbox do end
    end

    assert_match(/<input/, output, "Is an input element")
    assert_match(/<[^>]+type="checkbox"/, output, "Is a checkbox")
    assert_match(/<[^>]+value="true"/, output, "Is set to true")
    assert_match(/<[^>]+name="1"/, output, "Has a name")
  end

  def test_checkbox_not_checked
    output = render do |r|
      r.checkbox(false) do end
    end

    assert_match(/<input/, output, "Is an input element")
    assert_match(/<[^>]+type="checkbox"/, output, "Is a checkbox")
    assert_match(/<[^>]+value="true"/, output, "Is set to true")
    assert_match(/<[^>]+name="1"/, output, "Has a name")
  end

  def test_default_action
    assert_renders("<input name=\"1\" type=\"hidden\">") do |r|
      r.default_action do end
    end
  end

  def test_default_action_on
    obj = Object.new

    assert_renders("<input name=\"1\" type=\"hidden\">") do |r|
      r.default_action_on :foo, obj
    end
  end

  def test_file_upload
    assert_renders("<input name=\"1\" type=\"file\">") do |r|
      r.file_upload do end
    end
  end

  def test_form
    expected = "<form method=\"POST\" action=\"http://example.com/\"></form>" 
    assert_renders(expected) do |r|
      r.form do end
    end
  end

  def test_label_for
    rendering_context = Borges::RenderingContext.new(HtmlDocument.new,
                                                     @url,
                                                     CallbackStore.new)
    r = ::Borges::HtmlRenderer.new(rendering_context)

    assert_equal "Text", r.label_for(:text)
    assert_equal "Text Is Cool", r.label_for(:text_is_cool)
    assert_equal "of in at a or to by", r.label_for(:of_in_at_a_or_to_by)
  end

  def test_option_selected
    output = render do |r|
      r.option("text", true) do end
    end

    assert_match(/<option/, output, "Is an <output> element")
    assert_match(/selected="selected"/, output, "Output is selected")
    assert_match(/value="1"/, output, "Output a value")
    assert_match(/>text</, output, "Contents are text")
  end

  def test_option_not_selected
    assert_renders("<option value=\"1\">text</option>") do |r|
      r.option("text", false) do end
    end
  end

  def test_option
    assert_renders("<option value=\"1\">text</option>") do |r|
      r.option("text") do end
    end
  end
  
  def test_password_input
    expected = "<input name=\"1\" value=\"\" type=\"password\">"
    assert_renders(expected) do |r|
      r.password_input do end
    end
  end

  def test_password_input_with_value
    expected = "<input name=\"1\" value=\"text\" type=\"password\">"
    assert_renders(expected) do |r|
      r.password_input("text") do end
    end
  end

  def test_password_input_on
    obj = Object.new
    obj.instance_eval "def foo() 'text' end"

    expected = "<input id=\"foo\" name=\"1\" value=\"********\" type=\"password\">"
    assert_renders(expected) do |r|
      r.password_input_on :foo, obj
    end
  end

  def test_password_input_on_empty
    obj = Object.new
    obj.instance_eval "def foo() '' end"

    expected = "<input id=\"foo\" name=\"1\" value=\"\" type=\"password\">"
    assert_renders(expected) do |r|
      r.password_input_on :foo, obj
    end
  end

  def test_password_input_on_nil
    obj = Object.new
    obj.instance_eval "def foo() nil end"

    expected = "<input id=\"foo\" name=\"1\" value=\"\" type=\"password\">"
    assert_renders(expected) do |r|
      r.password_input_on :foo, obj
    end
  end

  def test_radio_button
    output = render do |r|

      # In real code, this would be:
      #
      # group = r.radio_group
      # r.radio_button(group, true) do ... end

      r.radio_button("group") do end
    end

    assert_match(/<input/, output, "Is an input element")
    assert_match(/type="radio"/, output, "Is a radio button")
    assert_match(/value="1"/, output, "Has a value")
    assert_match(/name="group"/, output, "Is of group \"group\"")
  end

  def test_radio_button_selected
    output = render do |r|
      r.radio_button("group", true) do end
    end

    assert_match(/<input/, output, "Is an input element")
    assert_match(/type="radio"/, output, "Is a radio button")
    assert_match(/value="1"/, output, "Has a value")
    assert_match(/name="group"/, output, "Is of group \"group\"")
    assert_match(/checked="checked"/, output, "Is selected")
  end

  def test_radio_button_not_selected
    output = render do |r|
      r.radio_button("group", false) do end
    end

    assert_match(/<input/, output, "Is an input element")
    assert_match(/type="radio"/, output, "Is a radio button")
    assert_match(/value="1"/, output, "Has a value")
    assert_match(/name="group"/, output, "Is of group \"group\"")
  end

  def test_select
    expected = "<select name=\"1\"><option value=\"2\">1</option><option value=\"3\">2</option></select>"
    assert_renders(expected) do |r|
      r.select([1, 2]) do end
    end
  end

  def test_select_selected
    output = render do |r|
      r.select([1], 1) do end
    end

    assert_match(/option[^>]+selected/, output)
  end

  def test_select_labeled
    expected = "<select name=\"1\"><option value=\"2\">One</option><option value=\"3\">Two</option></select>"
    assert_renders(expected) do |r|
      r.select({1 => 'One', 2 => 'Two'}) do end
    end
  end

  def test_style
    # TODO: write test
  end

  def test_submit_button
    expected = "<input value=\"Submit\" name=\"1\" type=\"submit\">"
    assert_renders(expected) do |r|
      r.submit_button do end
    end
  end

  def test_submit_button_with_value
    expected = "<input value=\"Next\" name=\"1\" type=\"submit\">"
    assert_renders(expected) do |r|
      r.submit_button("Next") do end
    end
  end

  def test_submit_button_on
    expected = "<input id=\"next\" value=\"Next\" name=\"1\" type=\"submit\">"
    assert_renders(expected) do |r|
      r.submit_button_on :next, Object.new
    end
  end

  def test_text_area
    expected = "<textarea name=\"1\"></textarea>"
    assert_renders(expected) do |r|
      r.text_area do end
    end
  end

  def test_text_area_safe
    expected = "<textarea name=\"1\">&amp;</textarea>"
    assert_renders(expected, "Escape unsafe characters") do |r|
      r.text_area("&") do end
    end
  end

  def test_text_area_with_text
    expected = "<textarea name=\"1\">text</textarea>"
    assert_renders(expected) do |r|
      r.text_area("text") do end
    end
  end

  def test_text_area_on
    obj = Object.new
    obj.instance_eval "def foo() 'text' end"

    expected = "<textarea id=\"foo\" name=\"1\">text</textarea>"

    output = render do |r|
      r.text_area_on :foo, obj
    end

    assert_match(/<textarea/, output, "Is a textarea element")
    assert_match(/id="foo"/, output, "Has id of \"foo\"")
    assert_match(/name="1"/, output, "Has a name")
    assert_match(/>text</, output, "Has the correct value")
  end
  
  def test_image_anchor
    assert_renders("<a href=\"#{@url}?1\"><img src=\"test.gif\" alt=\"foobar\"></a>") do |r|
      r.image_anchor("test.gif", "foobar") do end
    end
  end

  def test_text_input
    expected = "<input name=\"1\" value=\"\" type=\"text\">"
    assert_renders(expected) do |r|
      r.text_input do end
    end
  end

  def test_text_input_with_value
    expected = "<input name=\"1\" value=\"text\" type=\"text\">"
    assert_renders(expected) do |r|
      r.text_input("text") do end
    end
  end

  def test_text_input_on
    obj = Object.new
    obj.instance_eval "def foo() 'text' end"

    expected = "<input id=\"foo\" name=\"1\" value=\"text\" type=\"text\">"
    assert_renders(expected) do |r|
      r.text_input_on :foo, obj
    end
  end

  def test_url_for_document
    # TODO: write test
  end

  def test_value_input
    # TODO: write test
  end

=begin
  def test_anchorWithAction_do
    flunk "anchorWithAction_do"
  end

  def test_anchorWithAction_form
    flunk "anchorWithAction_form"
  end

  def test_anchorWithDocument_mimeType_text
    flunk "anchorWithDocument_mimeType_text"
  end

  def test_anchorWithDocument_text
    flunk "anchorWithDocument_text"
  end

  def test_booleanMenuWithValue_callback
    flunk "booleanMenuWithValue_callback"
  end

  def test_checkboxOn_of
    flunk "checkboxOn_of"
  end

  def test_hiddenInputWithValue_callback
    flunk "hiddenInputWithValue_callback"
  end

  def test_imageMapWithAction_form
    flunk "imageMapWithAction_form"
  end

  def test_imageWithForm
    flunk "imageWithForm"
  end

  def test_labelledRowForCheckboxOn_of
    flunk "labelledRowForCheckboxOn_of"
  end

  def test_labelledRowForList_on_of
    flunk "labelledRowForList_on_of"
  end

  def test_labelledRowForTextAreaOn_of
    flunk "labelledRowForTextAreaOn_of"
  end

  def test_labelledRowForTextInputOn_of
    flunk "labelledRowForTextInputOn_of"
  end

  def test_labelledRowForTextInputOn_of_size
    flunk "labelledRowForTextInputOn_of_size"
  end

  def test_linkWithScript
    flunk "linkWithScript"
  end

  def test_openAnchorWithDocument_mimeType
    flunk "openAnchorWithDocument_mimeType"
  end

  def test_parseImageMap
    flunk "parseImageMap"
  end

  def test_passwordInputWithCallback
    flunk "passwordInputWithCallback"
  end

  def test_selectFromList_selected_callback
    flunk "selectFromList_selected_callback"
  end

  def test_selectInputOn_of_list
    flunk "selectInputOn_of_list"
  end

  def test_textAreaOn_of
    flunk "textAreaOn_of"
  end

  def test_textInputOn_of
    flunk "textInputOn_of"
  end

  def test_textInputWithCallback
    flunk "textInputWithCallback"
  end

  def test_urlForDocument
    flunk "urlForDocument"
  end
=end

end # class HtmlRendererTest

