//
//  AttributeParseTest.swift
//  SwiftSoup
//
//  Created by Nabil Chatbi on 10/11/16.
//
/**
Test suite for attribute parser.
*/

import SwiftSoup
import XCTest

class AttributeParseTest: XCTestCase {
  func testparsesRoughAttributeString() throws {
    let html: String =
      "<a id=\"123\" class=\"baz = 'bar'\" style = 'border: 2px'qux zim foo = 12 mux=18 />"
    // should be: <id=123>, <class=baz = 'bar'>, <qux=>, <zim=>, <foo=12>, <mux.=18>

    let el: Element = try SwiftSoup.parse(html).getElementsByTag("a").get(0)
    let attr: Attributes = el.getAttributes()!
    XCTAssertEqual(7, attr.size())
    XCTAssertEqual("123", attr.get(key: "id"))
    XCTAssertEqual("baz = 'bar'", attr.get(key: "class"))
    XCTAssertEqual("border: 2px", attr.get(key: "style"))
    XCTAssertEqual("", attr.get(key: "qux"))
    XCTAssertEqual("", attr.get(key: "zim"))
    XCTAssertEqual("12", attr.get(key: "foo"))
    XCTAssertEqual("18", attr.get(key: "mux"))
  }

  func testhandlesNewLinesAndReturns() throws {
    let html: String = "<a\r\nfoo='bar\r\nqux'\r\nbar\r\n=\r\ntwo>One</a>"
    let el: Element = try SwiftSoup.parse(html).select("a").first()!
    XCTAssertEqual(2, el.getAttributes()?.size())
    XCTAssertEqual("bar\r\nqux", try el.attr("foo"))  // currently preserves newlines in quoted attributes. todo confirm if should.
    XCTAssertEqual("two", try el.attr("bar"))
  }

  func testparsesEmptyString() throws {
    let html: String = "<a />"
    let el: Element = try SwiftSoup.parse(html).getElementsByTag("a").get(0)
    let attr: Attributes = el.getAttributes()!
    XCTAssertEqual(0, attr.size())
  }

  func testcanStartWithEq() throws {
    let html: String = "<a =empty />"
    let el: Element = try SwiftSoup.parse(html).getElementsByTag("a").get(0)
    let attr: Attributes = el.getAttributes()!
    XCTAssertEqual(1, attr.size())
    XCTAssertTrue(attr.hasKey(key: "=empty"))
    XCTAssertEqual("", attr.get(key: "=empty"))
  }

  func teststrictAttributeUnescapes() throws {
    let html: String =
      "<a id=1 href='?foo=bar&mid&lt=true'>One</a> <a id=2 href='?foo=bar&lt;qux&lg=1'>Two</a>"
    let els: Elements = try SwiftSoup.parse(html).select("a")
    XCTAssertEqual("?foo=bar&mid&lt=true", try els.first()!.attr("href"))
    XCTAssertEqual("?foo=bar<qux&lg=1", try els.last()!.attr("href"))
  }

  func testmoreAttributeUnescapes() throws {
    let html: String = "<a href='&wr_id=123&mid-size=true&ok=&wr'>Check</a>"
    let els: Elements = try SwiftSoup.parse(html).select("a")
    XCTAssertEqual("&wr_id=123&mid-size=true&ok=&wr", try els.first()!.attr("href"))
  }

  func testQueryStringAttributeKeepsAmpersands() throws {
    let html = "<a href=\"/search?q=1&x=2&y=3\"></a>"
    let el: Element = try SwiftSoup.parse(html).select("a").first()!
    XCTAssertEqual("/search?q=1&x=2&y=3", try el.attr("href"))
  }

  func testQueryStringAttributeDecodesEntities() throws {
    let html = "<a href=\"/search?q=1&amp;x=2&amp;y=3\"></a>"
    let el: Element = try SwiftSoup.parse(html).select("a").first()!
    XCTAssertEqual("/search?q=1&x=2&y=3", try el.attr("href"))
  }

  func testparsesBooleanAttributes() throws {
    let html: String = "<a normal=\"123\" boolean empty=\"\"></a>"
    let el: Element = try SwiftSoup.parse(html).select("a").first()!

    XCTAssertEqual("123", try el.attr("normal"))
    XCTAssertEqual("", try el.attr("boolean"))
    XCTAssertEqual("", try el.attr("empty"))

    let attributes: [Attribute] = el.getAttributes()!.asList()
    XCTAssertEqual(3, attributes.count, "There should be 3 attribute present")

    // Assuming the list order always follows the parsed html
    XCTAssertFalse(
      (attributes[0] as? BooleanAttribute) != nil, "'normal' attribute should not be boolean")
    XCTAssertTrue(
      (attributes[1] as? BooleanAttribute) != nil, "'boolean' attribute should be boolean")
    XCTAssertFalse(
      (attributes[2] as? BooleanAttribute) != nil, "'empty' attribute should not be boolean")

    XCTAssertEqual(html, try el.outerHtml())
  }

  func testParsesMultibyteAttributeValues() throws {
    let html = "<a title=\"你&amp;好\" href=hello&amp;world data=πβ></a>"
    let el: Element = try SwiftSoup.parse(html).select("a").first()!
    XCTAssertEqual("你&好", try el.attr("title"))
    XCTAssertEqual("hello&world", try el.attr("href"))
    XCTAssertEqual("πβ", try el.attr("data"))
  }

  func testBeforeAttributeNameSkipsWhitespace() throws {
    let html = "<a  \t\r\nfoo=bar></a>"
    let el: Element = try SwiftSoup.parse(html).select("a").first()!
    XCTAssertEqual("bar", try el.attr("foo"))
  }

  func testretainsSlashFromAttributeName() throws {
    let html: String = "<img /onerror='doMyJob'/>"
    var doc: Document = try SwiftSoup.parse(html)
    XCTAssertTrue(
      try doc.select("img[onerror]").size() != 0, "SelfClosingStartTag ignores last character")
    XCTAssertEqual("<img onerror=\"doMyJob\" />", try doc.body()!.html())

    doc = try SwiftSoup.parse(html, "", Parser.xmlParser())
    XCTAssertEqual("<img onerror=\"doMyJob\" />", try doc.html())
  }

  func testAttributeValueAfterQuotedWithFollowup() throws {
    let html = "<a href=\"x\"/p=1></a>"
    let doc = try SwiftSoup.parse(html)
    let a = try doc.select("a").first()!
    XCTAssertEqual("x", try a.attr("href"))
    XCTAssertEqual("1", try a.attr("p"))
  }

  func testUnquotedAttributeValueStartsWithEqualsOrLt() throws {
    var doc = try SwiftSoup.parse("<p foo==bar></p>")
    var p = try doc.select("p").first()!
    XCTAssertEqual("=bar", try p.attr("foo"))

    doc = try SwiftSoup.parse("<p foo=<bar></p>")
    p = try doc.select("p").first()!
    XCTAssertEqual("<bar", try p.attr("foo"))
  }

  func testAttributeNameWithNullGetsReplacement() throws {
    let html = "<p a\u{0000}b=1></p>"
    let doc = try SwiftSoup.parse(html)
    let p = try doc.select("p").first()!
    XCTAssertTrue(p.hasAttr("a\u{FFFD}b"))
    XCTAssertEqual("1", try p.attr("a\u{FFFD}b"))
  }

  func testAttributeNameIncludesQuoteCharacter() throws {
    let html = "<a data-abc\"=\"foo\"></a>"
    let doc = try SwiftSoup.parse(html)
    let a = try doc.select("a").first()!
    XCTAssertTrue(a.hasAttr("data-abc\""))
    XCTAssertEqual("foo", try a.attr("data-abc\""))
  }

  func testAttributeValuePreservesCommentTagText() throws {
    let html = "<div><comment><img src=\"</comment><img src=x onerror=alert(38)//\">x</div>"
    let doc = try SwiftSoup.parse(html)
    let img = try doc.select("img").first()!
    XCTAssertEqual("</comment><img src=x onerror=alert(38)//", try img.attr("src"))
  }
}
