Fantom

 

//
// Copyright (c) 2009, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   12 Jan 09  Andy Frank  Creation
//

using graphics
using web

**************************************************************************
** DomTest
**************************************************************************

@NoDoc class DomTest : Weblet
{
  override Void onGet()
  {
    res.headers["Content-Type"] = "text/html; charset=utf-8"
    out := res.out
    out.docType
    out.html
    out.head
      .title.w("Dom Test").titleEnd
      .includeJs(`/pod/sys/sys.js`)
      .includeJs(`/pod/concurrent/concurrent.js`)
      .includeJs(`/pod/graphics/graphics.js`)
      .includeJs(`/pod/web/web.js`)
      .includeJs(`/pod/dom/dom.js`)
      .style.w(
       ".hidden { display: none; }")
      .styleEnd
      .script.w(
       "function print(name)
        {
          var p = document.getElementById('tests');
          p.innerHTML = p.innerHTML + ' -- ' + name + '...<'+'br/>';
         }
         window.onload = function() {
          var results = document.getElementById('results');
          try
          {
            var test = fan.dom.DomTestClient.make();
            print('testEmpty');     test.testEmpty();
            print('testBasics');    test.testBasics();
            print('testAttrs');     test.testAttrs();
            print('testCreate');    test.testCreate();
            print('testAddRemove'); test.testAddRemove();
            print('testStyle');     test.testStyle();
            print('testSvg');       test.testSvg();
            results.style.color = 'green';
            results.innerHTML = 'All tests passed! [' + test.m_verifies + ' verifies]';
          }
          catch (err)
          {
            results.style.color = 'red';
            results.innerHTML = 'Test failed - ' + err;
          }
        }")
       .scriptEnd
       .headEnd

    out.body
      .h1.w("Dom Test").h1End
      .hr

    // testEmpty (use raw html so no whitespace nodes)
    out.w("<div></div>").nl

    // testBasics
    out.div("id='testBasics' class='hidden'")
      .p.w("alpha").pEnd
      .span.w("beta-1").spanEnd
      .span.w("beta-2").spanEnd
      .a(`#`).w("gamma").aEnd
      .divEnd

    // testAttrs
    out.div("id='testAttrs' class='hidden'")
      .input("type='text' name='alpha' value='foo'")
      .checkbox("name='beta' checked='checked'")
      .checkbox
      .div("class='a'").divEnd
      .div("class='a b'").divEnd
      .div.divEnd
      .divEnd

    // testAttrs
    out.div("id='testStyle' class='hidden'")
      .div("class='a'").divEnd
      .div("class='a b'").divEnd
      .div.divEnd
      .divEnd

    out.p.w("Running...").pEnd
     .p("id='tests'").pEnd
     .p("id='results'").pEnd

    out.bodyEnd.htmlEnd
  }
}

**************************************************************************
** DomTestClient
**************************************************************************

@Js @NoDoc internal class DomTestClient
{

//////////////////////////////////////////////////////////////////////////
// testEmpty
//////////////////////////////////////////////////////////////////////////

  Void testEmpty()
  {
    elem := Win.cur.doc.body.querySelector("div")  // testEmpty must be first div
    verifyEq(elem.ns,  `http://www.w3.org/1999/xhtml`)
    verifyEq(elem.id,  null)
    verifyEq(elem->id, "")
    verifyEq(elem.attrs.size, 0)
    verifyEq(elem.hasChildren, false)
    verifyEq(elem.text,   "")
    verifyEq(elem.size.h, 0f)  // w will vary...
  }

//////////////////////////////////////////////////////////////////////////
// testBasics
//////////////////////////////////////////////////////////////////////////

  Void testBasics()
  {
    elem := Win.cur.doc.elemById("testBasics")
    verify(elem != null)
    verifyEq(elem.size, Size(0,0))

    kids := elem.children
    verifyEq(kids.size, 4)
    verifyEq(kids[0].html.trim, "alpha")
    verifyEq(kids[1].html, "beta-1")
    verifyEq(kids[2].html, "beta-2")
    verifyEq(kids[3].html, "gamma")

    a := Win.cur.doc.querySelector("#testBasics :last-child")
    verify(a != null)
    verifyEq(a.tagName, "a")
    verifyEq(a.parent.id, "testBasics")

    spans := Win.cur.doc.querySelectorAll("#testBasics span")
    verifyEq(spans.size, 2)
    verifyEq(spans[0].tagName, "span")
    verifyEq(spans[1].tagName, "span")
  }

//////////////////////////////////////////////////////////////////////////
// testAttrs
//////////////////////////////////////////////////////////////////////////

  Void testAttrs()
  {
    top := Win.cur.doc.elemById("testAttrs")
    verify(top != null)

    // top <div>
    verifyEq(top.tagName, "div")
    verifyAttrProp(top, "id",    "testAttrs")
    verifyAttrProp(top, "name",  null)
    verifyAttrProp(top, "yabba", null)
    verifyEq(top->offsetTop, 0)
    verify(top->innerHTML.toStr.contains("<input type="))

    a := top.children[0]  // <input>
    b := top.children[1]  // <checkbox>
    c := top.children[2]  // <checkbox>
    d := top.children[3]  // <div>
    e := top.children[4]  // <div>
    f := top.children[5]  // <div>

    // a:<input>
    try
    {
      verifyEq(a.tagName, "input")
      verifyAttrProp(a, "id",       null, "")   // null for attr, "" for prop
      verifyAttrProp(a, "type",     "text")
      verifyAttrProp(a, "name",     "alpha")
      verifyAttrProp(a, "tabIndex", null, 0)    // <input> tab defaults to 0
      a->tabIndex = 1
      verifyAttrProp(a, "tabIndex", "1", 1)     // set prop updates both attr/prop
      verifyAttrProp(a, "value", "foo")
      a->value = "bar"                          // setting prop does not modify attr
      verifyAttrProp(a, "value", "foo", "bar")  //   orig="foo", modified="bar"
      verifyEq(a["value"], "foo")               //   orig
      verifyEq(a->defaultValue, "foo")          //   orig
    }
    finally { a->value = "foo" }  // make firefox re-entrant...

    // b:<checkbox>
    try
    {
      verifyEq(b.tagName, "input")
      verifyAttrProp(b, "type",  "checkbox")
      verifyAttrProp(b, "name",  "beta")
      verifyAttrProp(b, "value", null, "on")          // value attr not defined
      verifyAttrProp(b, "checked", "checked", true)   // bool attrs mirror name
      b->checked = false                              // setting prop does not modify attr
      verifyAttrProp(b, "checked", "checked", false)  //   orig=true, modified=false
      verifyEq(b["checked"], "checked")               //   orig
      verifyEq(b->defaultChecked, true)               //   orig
      b["checked"] = "checked"                        // setting attr does not modify prop
      verifyAttrProp(b, "checked", "checked", false)  //   same prop
    }
    finally { b->checked = true }  // make firefox re-entrant...

    // c:<checkbox>
    verifyEq(c.tagName, "input")
    verifyAttrProp(c, "type",  "checkbox")
    verifyAttrProp(c, "name",  null, "")       // name attr null; prop is "" for <inputs>
    verifyAttrProp(c, "value", null, "on")     // value prop appears meaningless across browsers for <checkbox>?
    verifyAttrProp(c, "checked", null, false)  // bool attrs mirror name

    // d:<div>
    verifyEq(d.tagName, "div")
    verifyAttrProp(d, "tabIndex", null, -1)    // non-inputs default to null/-1
    d->tabIndex = 0
    verifyAttrProp(d, "tabIndex", "0", 0)      // set prop updates both attr/prop

    // e:<div>
    verifyEq(e.tagName, "div")
    verifyAttr(e, "x", null)
    e["x"] = "123";    verifyAttr(e, "x", "123")
    e.removeAttr("x"); verifyAttr(e, "x", null)
    e["x"] = "abc";    verifyAttr(e, "x", "abc")
    e["x"] = null;     verifyAttr(e, "x", null)

    // f:<div>
    verifyEq(f.tagName, "div")
  }

//////////////////////////////////////////////////////////////////////////
// testCreate
//////////////////////////////////////////////////////////////////////////

  Void testCreate()
  {
    elem := Elem {}
    verifyEq(elem.ns, `http://www.w3.org/1999/xhtml`)
    verifyEq(elem.tagName, "div")

    elem = Elem("table") {}
    verifyEq(elem.tagName, "table")

    elem = Win.cur.doc.createElem("div")
    verifyEq(elem.ns, `http://www.w3.org/1999/xhtml`)
    verifyEq(elem.tagName, "div")

    elem = Win.cur.doc.createElem("div", ["class":"foo"])
    verifyEq(elem.tagName, "div")
    verifyEq(elem.style.classes, ["foo"])

    elem = Win.cur.doc.createElem("div", ["id":"cool", "name":"yay", "class":"foo"])
    verifyEq(elem.tagName, "div")
    verifyEq(elem["id"], "cool")
    verifyEq(elem["name"], "yay")
    verifyEq(elem["class"], "foo")

    // TODO: create with namespace
    // TODO: some SVG tests
  }

//////////////////////////////////////////////////////////////////////////
// testAddRemove
//////////////////////////////////////////////////////////////////////////

  Void testAddRemove()
  {
    elem := Elem {}
    verifyEq(elem.children.size, 0)
    verifyEq(elem.hasChildren, false)

    a := Elem { it.style.addClass("a") }
    b := Elem { it.style.addClass("b") }
    c := Elem { it.style.addClass("c") }

    elem.add(a)
    verifyEq(elem.children.size, 1)
    verifyEq(elem.children.first.style.classes, ["a"])

    elem.add(b)
    elem.add(c)
    verifyEq(elem.children.size, 3)
    verifyEq(elem.children[1].style.classes, ["b"])
    verifyEq(elem.children[2].style.classes, ["c"])

    elem.remove(b)
    verifyEq(elem.children.size, 2)
    verifyEq(elem.children[0].style.classes, ["a"])
    verifyEq(elem.children[1].style.classes, ["c"])

    elem.remove(c)
    verifyEq(elem.children.size, 1)
    verifyEq(elem.children[0].style.classes, ["a"])

    elem.remove(elem.children.first)
    verifyEq(elem.children.size, 0)

    elem.addAll([b,c])
    verifyEq(elem.children.size, 2)
    verifyEq(elem.hasChildren, true)
    elem.insertBefore(a, b)
    verifyEq(elem.children.size, 3)
    verifyEq(elem.children[0].style.classes, ["a"])
    verifyEq(elem.children[1].style.classes, ["b"])
    verifyEq(elem.children[2].style.classes, ["c"])

    elem.removeAll
    verifyEq(elem.children.size, 0)
    verifyEq(elem.hasChildren, false)
  }

//////////////////////////////////////////////////////////////////////////
// testStyle
//////////////////////////////////////////////////////////////////////////

  Void testStyle()
  {
    top := Win.cur.doc.elemById("testStyle")

    // class tests
    a := top.children[0]
    b := top.children[1]
    c := top.children[2]

    verifyEq(a.style.hasClass("a"), true)
    verifyEq(a.style.hasClass("b"), false)
    verifyEq(a.style.hasClass("c"), false)
    verifyEq(b.style.hasClass("a"), true)
    verifyEq(b.style.hasClass("b"), true)
    verifyEq(b.style.hasClass("c"), false)
    verifyEq(c.style.hasClass("a"), false)
    a.style.addClass("c")
    b.style.addClass("c")
    c.style.addClass("c")
    verifyEq(a.style.hasClass("c"), true)
    verifyEq(b.style.hasClass("c"), true)
    verifyEq(c.style.hasClass("c"), true)
    a.style.removeClass("a")
    b.style.removeClass("a")
    verifyEq(a.style.hasClass("a"), false)
    verifyEq(b.style.hasClass("a"), false)
    c.style.removeClass("c")
    verifyEq(c.style.hasClass("c"), false)
    verifyEq(b.style.classes, ["b", "c"])
    b.style.addClass("b")
    verifyEq(b.style.classes, ["b", "c"])
    b.style.removeClass("x")
    verifyEq(b.style.classes, ["b", "c"])

    // legacy test -- keep supporting this
    a.style.addClass("x y z")
    verifyEq(a.style.hasClass("x"), true)
    verifyEq(a.style.hasClass("y"), true)
    verifyEq(a.style.hasClass("z"), true)
    a.style.removeClass("y z")
    verifyEq(a.style.hasClass("x"), true)
    verifyEq(a.style.hasClass("y"), false)
    verifyEq(a.style.hasClass("z"), false)

    // style tests
    x := Elem {}
    x.style["padding"] =  "10px"; verifyEq(x.style["padding"], "10px")
    x.style->padding = "20px";    verifyEq(x.style->padding, "20px")

    x.style["background-color"] = "#f00"; verifyEq(x.style["background-color"], "rgb(255, 0, 0)")
    x.style->backgroundColor = "#0f0";    verifyEq(x.style->backgroundColor, "rgb(0, 255, 0)")

    x.style["border-bottom-color"] = "#00f"
    verifyEq(x.style->borderBottomColor, "rgb(0, 0, 255)")

    x.style.setAll([
      "padding": "3px",
      "margin":  "6px",
      "border":  "2px dotted #ff0"
    ])
    verifyEq(x.style->padding, "3px")
    verifyEq(x.style->margin,  "6px")
    verifyEq(x.style->border,  "2px dotted rgb(255, 255, 0)")

    x.style.setCss("padding: 5px; margin: 10px; border: 1px solid #0f0")
    verifyEq(x.style->padding, "5px")
    verifyEq(x.style->margin,  "10px")
    verifyEq(x.style->border,  "1px solid rgb(0, 255, 0)")
  }

//////////////////////////////////////////////////////////////////////////
// testSvg
//////////////////////////////////////////////////////////////////////////

  Void testSvg()
  {
    a := Svg.line(0, 0, 10, 10)
    verifyEq(a.ns, `http://www.w3.org/2000/svg`)
    verifyEq(a.tagName, "line")

    // svg prop routes to attr
    verifyEq(a->x1, "0")
    verifyEq(a->y1, "0")
    verifyEq(a->x2, "10")
    verifyEq(a->y2, "10")

    // svg setProp routes to setAttr
    a->x1 = 5
    a->y1 = 5
    verifyEq(a->x1, "5")
    verifyEq(a->y1, "5")

    // svg classNames
    verifyEq(a.style.classes, Str#.emptyList)
    a.style.addClass("a b c")
    verifyEq(a.style.classes, ["a", "b", "c"])
    verifyEq(a.style.hasClass("a"), true)
    verifyEq(a.style.hasClass("b"), true)
    verifyEq(a.style.hasClass("c"), true)
    a.style.removeClass("b c")
    verifyEq(a.style.classes, ["a"])
    verifyEq(a.style.hasClass("a"), true)
    verifyEq(a.style.hasClass("b"), false)
    verifyEq(a.style.hasClass("c"), false)
  }

//////////////////////////////////////////////////////////////////////////
// Support
//////////////////////////////////////////////////////////////////////////

  private Void verify(Bool v)
  {
    if (v) verifies++
    else throw Err("Test failed")
  }

  private Void verifyEq(Obj? a, Obj? b)
  {
    if (a == b) verifies++
    else throw Err("$a != $b")
  }

  private Void verifyAttrProp(Elem elem, Str name, Str? attrVal, Obj? propVal := null)
  {
    verifyAttr(elem, name, attrVal)
    verifyProp(elem, name, propVal ?: attrVal)
  }

  private Void verifyAttr(Elem elem, Str name, Str? val)
  {
    // echo("# $elem a[$name]: " + elem.attr(name) + "/" + elem.get(name))
    verifyEq(elem.attr(name), val)
    verifyEq(elem.get(name),  val)
    verifyEq(elem[name],      val)
  }

  private Void verifyProp(Elem elem, Str name, Obj? val)
  {
    // echo("# $elem p[$name]: " + elem.prop(name) + "/" + elem.trap(name))
    verifyEq(elem.prop(name), val)
    verifyEq(elem.trap(name), val)
  }

  private Int verifies := 0
}