function test_getElementsByTagName(context, element) {
  // TODO: getElementsByTagName("*")
  test(function() {
    assert_false(context.getElementsByTagName("html") instanceof NodeList,
                 "Should not return a NodeList")
    assert_true(context.getElementsByTagName("html") instanceof HTMLCollection,
                "Should return an HTMLCollection")
  }, "Interfaces")

  test(function() {
    var firstCollection = context.getElementsByTagName("html"),
        secondCollection = context.getElementsByTagName("html")
    assert_true(firstCollection !== secondCollection ||
                firstCollection === secondCollection)
  }, "Caching is allowed")

  test(function() {
    var l = context.getElementsByTagName("nosuchtag")
    l[5] = "foopy"
    assert_equals(l[5], undefined)
    assert_equals(l.item(5), null)
  }, "Shouldn't be able to set unsigned properties on a HTMLCollection (non-strict mode)")

  test(function() {
    var l = context.getElementsByTagName("nosuchtag")
    assert_throws_js(TypeError, function() {
      "use strict";
      l[5] = "foopy"
    })
    assert_equals(l[5], undefined)
    assert_equals(l.item(5), null)
  }, "Shouldn't be able to set unsigned properties on a HTMLCollection (strict mode)")

  test(function() {
    var l = context.getElementsByTagName("nosuchtag")
    var fn = l.item;
    assert_equals(fn, HTMLCollection.prototype.item);
    l.item = "pass"
    assert_equals(l.item, "pass")
    assert_equals(HTMLCollection.prototype.item, fn);
  }, "Should be able to set expando shadowing a proto prop (item)")

  test(function() {
    var l = context.getElementsByTagName("nosuchtag")
    var fn = l.namedItem;
    assert_equals(fn, HTMLCollection.prototype.namedItem);
    l.namedItem = "pass"
    assert_equals(l.namedItem, "pass")
    assert_equals(HTMLCollection.prototype.namedItem, fn);
  }, "Should be able to set expando shadowing a proto prop (namedItem)")

  test(function() {
    var t1 = element.appendChild(document.createElement("pre"));
    t1.id = "x";
    var t2 = element.appendChild(document.createElement("pre"));
    t2.setAttribute("name", "y");
    var t3 = element.appendChild(document.createElementNS("", "pre"));
    t3.setAttribute("id", "z");
    var t4 = element.appendChild(document.createElementNS("", "pre"));
    t4.setAttribute("name", "w");
    this.add_cleanup(function() {
      element.removeChild(t1)
      element.removeChild(t2)
      element.removeChild(t3)
      element.removeChild(t4)
    });

    var list = context.getElementsByTagName('pre');
    var pre = list[0];
    assert_equals(pre.id, "x");

    var exposedNames = { 'x': 0, 'y': 1, 'z': 2 };
    for (var exposedName in exposedNames) {
      assert_equals(list[exposedName], list[exposedNames[exposedName]]);
      assert_equals(list[exposedName], list.namedItem(exposedName));
      assert_true(exposedName in list, "'" + exposedName + "' in list");
      assert_true(list.hasOwnProperty(exposedName),
                  "list.hasOwnProperty('" + exposedName + "')");
    }

    var unexposedNames = ["w"];
    for (var unexposedName of unexposedNames) {
      assert_false(unexposedName in list);
      assert_false(list.hasOwnProperty(unexposedName));
      assert_equals(list[unexposedName], undefined);
      assert_equals(list.namedItem(unexposedName), null);
    }

    assert_array_equals(Object.getOwnPropertyNames(list).sort(),
                        ["0", "1", "2", "3", "x", "y", "z"]);

    var desc = Object.getOwnPropertyDescriptor(list, '0');
    assert_equals(typeof desc, "object", "descriptor should be an object");
    assert_true(desc.enumerable, "desc.enumerable");
    assert_true(desc.configurable, "desc.configurable");

    desc = Object.getOwnPropertyDescriptor(list, 'x');
    assert_equals(typeof desc, "object", "descriptor should be an object");
    assert_false(desc.enumerable, "desc.enumerable");
    assert_true(desc.configurable, "desc.configurable");
  }, "hasOwnProperty, getOwnPropertyDescriptor, getOwnPropertyNames")

  test(function() {
    assert_equals(document.createElementNS("http://www.w3.org/1999/xhtml", "i").localName, "i") // Sanity
    var t = element.appendChild(document.createElementNS("http://www.w3.org/1999/xhtml", "I"))
    this.add_cleanup(function() {element.removeChild(t)})
    assert_equals(t.localName, "I")
    assert_equals(t.tagName, "I")
    assert_equals(context.getElementsByTagName("I").length, 0)
    assert_equals(context.getElementsByTagName("i").length, 0)
  }, "HTML element with uppercase tagName never matches in HTML Documents")

  test(function() {
    var t = element.appendChild(document.createElementNS("test", "st"))
    this.add_cleanup(function() {element.removeChild(t)})
    assert_array_equals(context.getElementsByTagName("st"), [t])
    assert_array_equals(context.getElementsByTagName("ST"), [])
  }, "Element in non-HTML namespace, no prefix, lowercase name")

  test(function() {
    var t = element.appendChild(document.createElementNS("test", "ST"))
    this.add_cleanup(function() {element.removeChild(t)})
    assert_array_equals(context.getElementsByTagName("ST"), [t])
    assert_array_equals(context.getElementsByTagName("st"), [])
  }, "Element in non-HTML namespace, no prefix, uppercase name")

  test(function() {
    var t = element.appendChild(document.createElementNS("test", "te:st"))
    this.add_cleanup(function() {element.removeChild(t)})
    assert_array_equals(context.getElementsByTagName("st"), [])
    assert_array_equals(context.getElementsByTagName("ST"), [])
    assert_array_equals(context.getElementsByTagName("te:st"), [t])
    assert_array_equals(context.getElementsByTagName("te:ST"), [])
  }, "Element in non-HTML namespace, prefix, lowercase name")

  test(function() {
    var t = element.appendChild(document.createElementNS("test", "te:ST"))
    this.add_cleanup(function() {element.removeChild(t)})
    assert_array_equals(context.getElementsByTagName("st"), [])
    assert_array_equals(context.getElementsByTagName("ST"), [])
    assert_array_equals(context.getElementsByTagName("te:st"), [])
    assert_array_equals(context.getElementsByTagName("te:ST"), [t])
  }, "Element in non-HTML namespace, prefix, uppercase name")

  test(function() {
    var t = element.appendChild(document.createElement("aÇ"))
    this.add_cleanup(function() {element.removeChild(t)})
    assert_equals(t.localName, "aÇ")
    assert_array_equals(context.getElementsByTagName("AÇ"), [t], "All uppercase input")
    assert_array_equals(context.getElementsByTagName("aÇ"), [t], "Ascii lowercase input")
    assert_array_equals(context.getElementsByTagName("aç"), [], "All lowercase input")
  }, "Element in HTML namespace, no prefix, non-ascii characters in name")

  test(function() {
    var t = element.appendChild(document.createElementNS("test", "AÇ"))
    this.add_cleanup(function() {element.removeChild(t)})
    assert_array_equals(context.getElementsByTagName("AÇ"), [t])
    assert_array_equals(context.getElementsByTagName("aÇ"), [])
    assert_array_equals(context.getElementsByTagName("aç"), [])
  }, "Element in non-HTML namespace, non-ascii characters in name")

  test(function() {
    var t = element.appendChild(document.createElementNS("http://www.w3.org/1999/xhtml", "test:aÇ"))
    this.add_cleanup(function() {element.removeChild(t)})
    assert_array_equals(context.getElementsByTagName("TEST:AÇ"), [t], "All uppercase input")
    assert_array_equals(context.getElementsByTagName("test:aÇ"), [t], "Ascii lowercase input")
    assert_array_equals(context.getElementsByTagName("test:aç"), [], "All lowercase input")
  }, "Element in HTML namespace, prefix, non-ascii characters in name")

  test(function() {
    var t = element.appendChild(document.createElementNS("test", "TEST:AÇ"))
    this.add_cleanup(function() {element.removeChild(t)})
    assert_array_equals(context.getElementsByTagName("TEST:AÇ"), [t], "All uppercase input")
    assert_array_equals(context.getElementsByTagName("test:aÇ"), [], "Ascii lowercase input")
    assert_array_equals(context.getElementsByTagName("test:aç"), [], "All lowercase input")
  }, "Element in non-HTML namespace, prefix, non-ascii characters in name")

  test(function() {
    var actual = context.getElementsByTagName("*");
    var expected = [];
    var get_elements = function(node) {
      for (var i = 0; i < node.childNodes.length; i++) {
        var child = node.childNodes[i];
        if (child.nodeType === child.ELEMENT_NODE) {
          expected.push(child);
          get_elements(child);
        }
      }
    }
    get_elements(context);
    assert_array_equals(actual, expected);
  }, "getElementsByTagName('*')")

  test(function() {
    var t1 = element.appendChild(document.createElement("abc"));
    this.add_cleanup(function() {element.removeChild(t1)});

    var l = context.getElementsByTagName("abc");
    assert_true(l instanceof HTMLCollection);
    assert_equals(l.length, 1);

    var t2 = element.appendChild(document.createElement("abc"));
    assert_equals(l.length, 2);

    element.removeChild(t2);
    assert_equals(l.length, 1);
  }, "getElementsByTagName() should be a live collection");
}
