From d677ade2f9992c7c9584f905d62ef110731c0326 Mon Sep 17 00:00:00 2001 From: Ms2ger <ms2ger@gmail.com> Date: Mon, 4 Jun 2012 16:49:57 -0700 Subject: [PATCH] Bug 591467 - Add tests for microdata --- dom/imptests/Makefile.in | 1 + .../submission/Opera/microdata/Makefile.in | 19 + .../Opera/microdata/test_001.html.json | 8 + dom/imptests/html.mk | 1 + dom/imptests/html.txt | 1 + .../submission/Opera/microdata/Makefile.in | 24 + .../submission/Opera/microdata/test_001.html | 3652 +++++++++++++++++ 7 files changed, 3706 insertions(+) create mode 100644 dom/imptests/failures/html/tests/submission/Opera/microdata/Makefile.in create mode 100644 dom/imptests/failures/html/tests/submission/Opera/microdata/test_001.html.json create mode 100644 dom/imptests/html/tests/submission/Opera/microdata/Makefile.in create mode 100644 dom/imptests/html/tests/submission/Opera/microdata/test_001.html diff --git a/dom/imptests/Makefile.in b/dom/imptests/Makefile.in index f41498cf5c014..221f6f3b189d9 100644 --- a/dom/imptests/Makefile.in +++ b/dom/imptests/Makefile.in @@ -14,6 +14,7 @@ DIRS = \ failures/webapps/WebStorage/tests/submissions/Ms2ger \ failures/webapps/WebStorage/tests/submissions/Infraware \ failures/webapps/DOMCore/tests/submissions/Opera \ + failures/html/tests/submission/Opera/microdata \ $(NULL) include $(srcdir)/editing.mk diff --git a/dom/imptests/failures/html/tests/submission/Opera/microdata/Makefile.in b/dom/imptests/failures/html/tests/submission/Opera/microdata/Makefile.in new file mode 100644 index 0000000000000..cb8d8f6ebbce4 --- /dev/null +++ b/dom/imptests/failures/html/tests/submission/Opera/microdata/Makefile.in @@ -0,0 +1,19 @@ +DEPTH = ../../../../../../../.. + +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = dom/imptests/failures/html/tests/submission/Opera/microdata + +DIRS = \ + $(NULL) + +include $(DEPTH)/config/autoconf.mk +include $(topsrcdir)/config/rules.mk + +_TESTS = \ + test_001.html.json \ + $(NULL) + +libs:: $(_TESTS) + $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir) diff --git a/dom/imptests/failures/html/tests/submission/Opera/microdata/test_001.html.json b/dom/imptests/failures/html/tests/submission/Opera/microdata/test_001.html.json new file mode 100644 index 0000000000000..c2bb1886df44b --- /dev/null +++ b/dom/imptests/failures/html/tests/submission/Opera/microdata/test_001.html.json @@ -0,0 +1,8 @@ +{ + "document.getItems must return a NodeList": true, + "itemValue must reflect the src attribute on track elements": true, + "itemValue must reflect the textContent of time elements with no datetime attribute": true, + "itemValue must reflect the datetime attribute of time elements with a datetime attribute": true, + "the namedItem property must be read/write": true +} + diff --git a/dom/imptests/html.mk b/dom/imptests/html.mk index 14ee9edf4673a..c5ca3fd42f3ca 100644 --- a/dom/imptests/html.mk +++ b/dom/imptests/html.mk @@ -1,3 +1,4 @@ DIRS += \ html/tests/submission/Mozilla \ + html/tests/submission/Opera/microdata \ $(NULL) diff --git a/dom/imptests/html.txt b/dom/imptests/html.txt index f45a59dba915f..c3c92caf36c75 100644 --- a/dom/imptests/html.txt +++ b/dom/imptests/html.txt @@ -1,2 +1,3 @@ https://dvcs.w3.org/hg/html|html tests/submission/Mozilla +tests/submission/Opera/microdata diff --git a/dom/imptests/html/tests/submission/Opera/microdata/Makefile.in b/dom/imptests/html/tests/submission/Opera/microdata/Makefile.in new file mode 100644 index 0000000000000..1b42abe7288b2 --- /dev/null +++ b/dom/imptests/html/tests/submission/Opera/microdata/Makefile.in @@ -0,0 +1,24 @@ +# THIS FILE IS AUTOGENERATED BY importTestsuite.py - DO NOT EDIT + +DEPTH = ../../../../../../.. + +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = dom/imptests/html/tests/submission/Opera/microdata + +DIRS = \ + $(NULL) + +include $(DEPTH)/config/autoconf.mk +include $(topsrcdir)/config/rules.mk + +_TESTS = \ + test_001.html \ + $(NULL) + +_TESTS += \ + $(NULL) + +libs:: $(_TESTS) + $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir) diff --git a/dom/imptests/html/tests/submission/Opera/microdata/test_001.html b/dom/imptests/html/tests/submission/Opera/microdata/test_001.html new file mode 100644 index 0000000000000..cf583293be2d0 --- /dev/null +++ b/dom/imptests/html/tests/submission/Opera/microdata/test_001.html @@ -0,0 +1,3652 @@ +<!doctype html> +<html> + <head> + <meta charset="UTF-8"> + <title>Microdata tests</title> + <script type="text/javascript" src="/resources/testharness.js"></script> + <script type="text/javascript" src="/resources/testharnessreport.js"></script> + </head> + <body> + <noscript><p>Enable JavaScript and reload</p></noscript> + <div id="log">Running test...</div> + <div itemscope itemtype="http://example.com/bar data:text/plain, http://example.com/foo" id="one"></div> + <div itemscope itemtype="http://example.com/bar" id="two"></div> + <div itemscope itemtype="http://example.com/foo http://example.com/bar" id="three"> + <div itemscope itemtype="http://example.com/bar data:text/plain," id="four"></div> + </div> + <div itemscope id="five"></div> + <script type="text/javascript"> +/* All tests are stand-alone. +To reduce this testsuite to show only a single desired test, +simply remove all test(...) blocks before and after it. */ + +function makeEl(eltype,props,contents) { + var elem = document.createElement(eltype); + for( var i in props ) { + //just in case the framework extends object... + if( props.hasOwnProperty(i) ) { + elem.setAttribute(i,props[i]); + } + } + if( contents ) { + elem.innerHTML = contents; + } + return elem; +} + +/* getItem tests */ +test(function () { + assert_true( !!document.getItems ); +}, 'document.getItems must exist'); +test(function () { + assert_true( document.getItems() instanceof NodeList, 'instanceof test' ); + NodeList.prototype.customProperty = true; + assert_true( document.getItems().customProperty, 'inheritance test' ); +}, 'document.getItems must return a NodeList'); +test(function () { + assert_equals( document.getItems().length, 5 ); +}, 'document.getItems must locate the correct number of items'); +test(function () { + var nlist = document.getItems(); + var foo = makeEl('div',{itemscope:'itemscope'}); + document.body.appendChild(foo); + var templength = nlist.length; + document.body.removeChild(foo); + assert_equals( templength, 6 ); + assert_equals( nlist.length, 5 ); +}, 'document.getItems must return a live NodeList'); +test(function () { + var nlist = document.getItems(); + document.getElementById('one').removeAttribute('itemscope'); + var templength = nlist.length; + document.getElementById('one').setAttribute('itemscope','itemscope'); + assert_equals( templength, 4 ); + assert_equals( nlist.length, 5 ); +}, 'live NodeList must notice when itemscope changes'); +test(function () { + document.getElementById('one').removeAttribute('itemscope'); + var templength = document.getItems().length; + document.getElementById('one').setAttribute('itemscope','itemscope'); + assert_equals( templength, 4 ); + assert_equals( document.getItems().length, 5 ); +}, 'next request must notice when itemscope changes'); +test(function () { + assert_equals( document.getItems('http://example.com/').length, 0, 'http://example.com/' ); + assert_equals( document.getItems('example').length, 0, 'example' ); + assert_equals( document.getItems('http://example.com/foo').length, 2, 'http://example.com/foo' ); + assert_equals( document.getItems('http://example.com/bar').length, 4, 'http://example.com/bar' ); + assert_equals( document.getItems('data:text/plain,').length, 2, 'data:text/plain,' ); +}, 'document.getItems must locate the right number of items for each itemtype'); +test(function () { + assert_equals( document.getItems('http://example.com/Foo').length, 0, 'http://example.com/Foo' ); + assert_equals( document.getItems('HTTP://example.com/foo').length, 0, 'HTTP://example.com/foo' ); +}, 'document.getItems must be case sensitive'); +test(function () { + var nlist = document.getItems('http://example.com/foo'); + var foo = makeEl('div',{itemscope:'itemscope',itemtype:'http://example.com/foo'}); + document.body.appendChild(foo); + var templength = nlist.length; + document.body.removeChild(foo); + assert_equals( templength, 3 ); + assert_equals( nlist.length, 2 ); +}, 'document.getItems must return a live NodeList when using URLs'); +test(function () { + var nlist = document.getItems('http://example.com/foo'); + document.getElementById('one').removeAttribute('itemtype'); + var templength = nlist.length; + document.getElementById('one').setAttribute('itemtype','http://example.com/bar data:text/plain, http://example.com/foo'); + assert_equals( templength, 1 ); + assert_equals( nlist.length, 2 ); +}, 'live NodeList must notice when itemtype changes'); +test(function () { + document.getElementById('one').removeAttribute('itemtype'); + var templength = document.getItems('http://example.com/foo').length; + document.getElementById('one').setAttribute('itemtype','http://example.com/bar data:text/plain, http://example.com/foo'); + assert_equals( templength, 1 ); + assert_equals( document.getItems('http://example.com/foo').length, 2 ); +}, 'next request must notice when itemtype changes'); +test(function () { + assert_equals( document.getItems('http://example.com/foo data:text/plain,').length, 1, 'basic spaces' ); + assert_equals( document.getItems(' http://example.com/foo data:text/plain, ').length, 1, 'extraneous spaces' ); +}, 'document.getItems must locate items when parameters are separated by spaces'); +test(function () { + assert_equals( document.getItems('http://example.com/foo data:text/plain, http://example.com/foo').length, 1 ); +}, 'document.getItems must ignore duplicated tokens'); +test(function () { + var testitems = document.getItems('http://example.com/bar'); + assert_equals( testitems[0].id, 'one' ); + assert_equals( testitems[1].id, 'two' ); + assert_equals( testitems[2].id, 'three' ); + assert_equals( testitems[3].id, 'four' ); +}, 'document.getItems NodeList must be in source tree order'); +test(function () { + assert_true( document.getItems('http://example.com/abc') != document.getItems('http://example.com/def'), 'different tokens' ); + assert_true( document.getItems() != document.getItems(' '), 'no tokens' ); +}, 'document.getItems must not return the same NodeList for different parameters'); +test(function () { + assert_equals( document.getItems('').length, 5, 'empty string' ); + assert_equals( document.getItems(' ').length, 5, 'string with spaces' ); +}, 'document.getItems must treat no tokens as no parameter'); +//removed due to disputed Web compatibility of casting null with DOM methods +/* +test(function () { + assert_equals( document.getItems(null).length, 0, 'null' ); + assert_equals( document.getItems(window.undefined).length, 0, 'undefined' ); +}, 'document.getItems must cast null and undefined to strings'); +*/ +test(function () { + var foo = makeEl('div',{itemtype:'http://example.com/foo'}); + document.body.appendChild(foo); + var templength = document.getItems('http://example.com/foo').length; + document.body.removeChild(foo); + assert_equals( templength, 2 ); +}, 'document.getItems must not find items with itemtype but not itemscope'); +test(function () { + var foo = makeEl('div',{itemscope:'itemscope',itemtype:'baz'}), + bar = makeEl('div',{itemscope:'itemscope',itemtype:location.href.replace(/\/[^\/]*$/,'/baz')}); + document.body.appendChild(foo); + document.body.appendChild(bar); + var unrezlength = document.getItems('baz').length; + var rezlength = document.getItems(location.href.replace(/\/[^\/]*$/,'/baz')).length; + document.body.removeChild(foo); + document.body.removeChild(bar); + assert_equals( unrezlength, 1, 'unresolved URL' ); + assert_equals( rezlength, 1, 'resolved URL' ); +}, 'document.getItems and itemtype must not resolve URLs'); +test(function () { + document.getElementById('one').setAttribute('itemprop','test'); + document.getElementById('four').setAttribute('itemprop','test'); + var templength = document.getItems().length; + document.getElementById('one').removeAttribute('itemprop'); + document.getElementById('four').removeAttribute('itemprop'); + assert_equals( templength, 3 ); +}, 'document.getItems must not see items that have the itemprop attribute set'); + +/* itemScope property tests */ +test(function () { + assert_true(makeEl('div',{itemscope:'itemscope'}).itemScope); + assert_false(makeEl('div',{}).itemScope); +}, 'the itemscope attribute must be reflected by the .itemScope property'); +test(function () { + assert_equals( typeof makeEl('div',{itemscope:'itemscope'}).itemScope, 'boolean', 'attribute exists' ); + assert_equals( typeof makeEl('div',{}).itemScope, 'boolean', 'attribute does not exist' ); +}, 'the itemScope property must be boolean'); +test(function () { + var testEl = makeEl('div',{}); + testEl.itemScope = true; + assert_true(testEl.itemScope,'writing true'); + testEl.itemScope = false; + assert_false(testEl.itemScope,'writing false'); +}, 'the itemScope property must be read/write'); +test(function () { + var testEl = makeEl('div',{}); + testEl.itemScope = true; + assert_true(testEl.hasAttribute('itemscope'),'writing true'); + testEl.itemScope = false; + assert_false(testEl.hasAttribute('itemscope'),'writing false'); +}, 'writing to the itemScope property must toggle existence of the itemscope content attribute'); +test(function () { + var testEl = makeEl('div',{}); + document.body.appendChild(testEl); + var numAppend = document.getItems().length; + testEl.itemScope = true; + var numTrue = document.getItems().length; + testEl.itemScope = false; + var numFalse = document.getItems().length; + document.body.removeChild(testEl); + assert_equals(numAppend,5,'after appending the new item'); + assert_equals(numTrue,6,'after setting the property to true'); + assert_equals(numFalse,5,'after setting the property to false'); +}, 'writing to the itemScope property must affect whether the element is returned by getItems'); +test(function () { + var testEl = makeEl('div',{}), nlist = document.getItems(); + document.body.appendChild(testEl); + var numAppend = nlist.length; + testEl.itemScope = true; + var numTrue = nlist.length; + testEl.itemScope = false; + var numFalse = nlist.length; + document.body.removeChild(testEl); + assert_equals(numAppend,5,'after appending the new item'); + assert_equals(numTrue,6,'after setting the property to true'); + assert_equals(numFalse,5,'after setting the property to false'); +}, 'writing to the itemScope property must affect membership of live NodeLists'); + +/* itemType property tests (properties collection tests are done later) */ +test(function () { + assert_equals(makeEl('div',{}).itemType.toString(),'','no attribute'); + assert_equals(makeEl('div',{itemtype:' foo bar '}).itemType.toString(),' foo bar ','with simple tokens'); + var testEl = makeEl('div',{itemtype:'foo'}); + testEl.removeAttribute('itemtype'); + assert_equals(testEl.itemType.toString(),'','removed attribute'); +}, 'the itemType attribute must be reflected by the .itemRef property'); +test(function () { + assert_equals( typeof makeEl('div',{}).itemType, 'object' ); +}, 'the itemType property must be an object'); +test(function () { + assert_true( makeEl('div',{}).itemType instanceof DOMTokenList, 'instanceof test' ); + DOMTokenList.prototype.customProperty = true; + assert_true( makeEl('div',{}).itemType.customProperty, 'inheritance test' ); +}, 'the itemType property must implement DOMTokenList'); +test(function () { + var testEl = makeEl('div',{}); + assert_equals( testEl.itemType, testEl.itemType ); +}, 'the itemType property must always reference the same object'); +test(function () { + assert_equals( makeEl('div',{itemtype:'test test'}).itemType.length, 2, 'duplicates in initial string should be preserved' ); + assert_equals( makeEl('div',{itemtype:'test test'}).itemType.item(0), 'test' ); + assert_true( makeEl('div',{itemtype:'test test'}).itemType.contains('test') ); +}, 'itemType must be correct for an element that has itemtype tokens'); +test(function () { + assert_equals( makeEl('div',{itemtype:' '}).itemType.length, 0 ); +}, 'itemType.length must be 0 for an element that has no tokens'); +test(function () { + assert_false( makeEl('div',{itemtype:' '}).itemType.contains('foo') ); +}, 'itemType must not contain an undefined class'); +test(function () { + assert_equals( makeEl('div',{itemtype:' '}).itemType.item(0), null ); +}, 'itemType.item() must return null for out-of-range index'); +test(function () { + assert_equals( makeEl('div',{itemtype:' '}).itemType.item(-1), null ); +}, 'itemType.item() must return null for negative index'); +test(function () { + /* the normative part of the spec states that: + "unless the length is zero, in which case there are no supported property indices" + ... + "The term[...] supported property indices [is] used as defined in the WebIDL specification." + WebIDL creates actual OwnProperties and then [] just acts as a normal property lookup */ + assert_equals( makeEl('div',{itemtype:' '}).itemType[0], window.undefined ); +}, 'itemType[index] must be undefined for out-of-range index'); +test(function () { + assert_equals( makeEl('div',{itemtype:' '}).itemType[-1], window.undefined ); +}, 'itemType[index] must be undefined for negative index'); +test(function () { + assert_equals( makeEl('div',{itemType:' '}).itemType.toString(), ' ' ); +}, 'empty itemType should stringify to contain the attribute\'s whitespace'); +test(function () { + assert_throws( 'SYNTAX_ERR', function () { makeEl('div',{itemtype:' '}).itemType.contains(''); } ); +}, '.contains(empty_string) must throw a SYNTAX_ERR'); +test(function () { + assert_throws( 'SYNTAX_ERR', function () { makeEl('div',{itemtype:' '}).itemType.add(''); } ); +}, '.add(empty_string) must throw a SYNTAX_ERR'); +test(function () { + assert_throws( 'SYNTAX_ERR', function () { makeEl('div',{itemtype:' '}).itemType.remove(''); } ); +}, '.remove(empty_string) must throw a SYNTAX_ERR'); +test(function () { + assert_throws( 'SYNTAX_ERR', function () { makeEl('div',{itemtype:' '}).itemType.toggle(''); } ); +}, '.toggle(empty_string) must throw a SYNTAX_ERR'); +test(function () { + assert_throws( 'INVALID_CHARACTER_ERR', function () { makeEl('div',{itemtype:' '}).itemType.contains('a b'); } ); +}, '.contains(string_with_spaces) must throw an INVALID_CHARACTER_ERR'); +test(function () { + assert_throws( 'INVALID_CHARACTER_ERR', function () { makeEl('div',{itemtype:' '}).itemType.add('a b'); } ); +}, '.add(string_with_spaces) must throw an INVALID_CHARACTER_ERR'); +test(function () { + assert_throws( 'INVALID_CHARACTER_ERR', function () { makeEl('div',{itemtype:' '}).itemType.remove('a b'); } ); +}, '.remove(string_with_spaces) must throw an INVALID_CHARACTER_ERR'); +test(function () { + assert_throws( 'INVALID_CHARACTER_ERR', function () { makeEl('div',{itemtype:' '}).itemType.toggle('a b'); } ); +}, '.toggle(string_with_spaces) must throw an INVALID_CHARACTER_ERR'); +test(function () { + var testEl = makeEl('div',{itemtype:'foo'}); + assert_true( testEl.itemType.contains('foo'), 'before change' ); + testEl.setAttribute('itemtype','bar'); + assert_true( testEl.itemType.contains('bar'), 'after change' ); + assert_false( testEl.itemType.contains('foo'), 'after change' ); +}, 'itemType.contains must update when the underlying attribute is changed'); +test(function () { + assert_false( makeEl('div',{itemtype:'foo'}).itemType.contains('FOO') ); +}, 'itemType.contains must be case sensitive'); +test(function () { + assert_false( makeEl('div',{itemtype:'foo'}).itemType.contains('foo.') ); + assert_false( makeEl('div',{itemtype:'foo'}).itemType.contains('foo)') ); + assert_false( makeEl('div',{itemtype:'foo'}).itemType.contains('foo\'') ); + assert_false( makeEl('div',{itemtype:'foo'}).itemType.contains('foo$') ); + assert_false( makeEl('div',{itemtype:'foo'}).itemType.contains('foo~') ); + assert_false( makeEl('div',{itemtype:'foo'}).itemType.contains('foo?') ); + assert_false( makeEl('div',{itemtype:'foo'}).itemType.contains('foo\\') ); +}, 'itemType.contains must not match when punctuation characters are added'); +test(function () { + var elem = makeEl('div',{itemtype:'foo'}); + elem.itemType.add('FOO'); + assert_true( elem.itemType.contains('foo') ); +}, 'itemType.add must not remove existing tokens'); +test(function () { + assert_true( makeEl('div',{itemtype:'foo FOO'}).itemType.contains('FOO') ); +}, 'itemType.contains case sensitivity must match a case-specific string'); +test(function () { + assert_equals( makeEl('div',{itemtype:'foo FOO'}).itemType.length, 2 ); +}, 'itemType.length must correctly reflect the number of tokens'); +test(function () { + assert_equals( makeEl('div',{itemtype:'foo FOO'}).itemType.item(0), 'foo' ); +}, 'itemType.item(0) must return the first token'); +test(function () { + var elem = makeEl('div',{itemtype:'foo'}); + elem.itemType.add('FOO'); + assert_equals( elem.itemType.item(1), 'FOO' ); +}, 'itemType.item must return case-sensitive strings and preserve token order'); +test(function () { + assert_equals( makeEl('div',{itemtype:'foo FOO'}).itemType[0], 'foo' ); +}, 'itemType[0] must return the first token'); +test(function () { + assert_equals( makeEl('div',{itemtype:'foo FOO'}).itemType[1], 'FOO' ); +}, 'itemType[index] must return case-sensitive strings and preserve token order'); +test(function () { + /* the normative part of the spec states that: + "unless the length is zero, in which case there are no supported property indices" + ... + "The term[...] supported property indices [is] used as defined in the WebIDL specification." + WebIDL creates actual OwnProperties and then [] just acts as a normal property lookup */ + assert_equals( makeEl('div',{itemtype:'foo FOO'}).itemType[2], window.undefined ); +}, 'itemType[index] must still be undefined for out-of-range index when earlier indexes exist'); +test(function () { + var elem = makeEl('div',{}); + elem.itemType.add('foo'); + elem.itemType.add('FOO'); + assert_equals( elem.getAttribute('itemtype'), 'foo FOO' ); +}, 'itemtype attribute must update correctly when items have been added through itemType'); +test(function () { + var elem = makeEl('div',{}); + elem.itemType.add('foo'); + elem.itemType.add('FOO'); + assert_equals( elem.itemType + '', 'foo FOO' ); +}, 'itemType must stringify correctly when items have been added'); +test(function () { + var elem = makeEl('div',{itemtype:'foo FOO'}); + elem.itemType.add('foo'); + assert_equals( elem.itemType.length, 2 ); + assert_equals( elem.itemType + '', 'foo FOO' ); +}, 'itemType.add must not make any changes if an existing token is added'); +test(function () { + var elem = makeEl('div',{itemtype:'foo FOO'}); + elem.itemType.remove('bar'); + assert_equals( elem.itemType.length, 2 ); + assert_equals( elem.itemType + '', 'foo FOO' ); +}, 'itemType.remove must not make any changes if a non-existing token is removed'); +test(function () { + var elem = makeEl('div',{itemtype:'foo FOO'}); + elem.itemType.remove('foo'); + assert_equals( elem.itemType.length, 1 ); + assert_equals( elem.itemType.toString(), 'FOO' ); + assert_false( elem.itemType.contains('foo') ); + assert_true( elem.itemType.contains('FOO') ); +}, 'itemType.remove must remove existing tokens'); +test(function () { + var elem = makeEl('div',{itemtype:'test test'}); + elem.itemType.remove('test'); + assert_equals( elem.itemType.length, 0 ); + assert_false( elem.itemType.contains('test') ); +}, 'itemType.remove must remove duplicated tokens'); +test(function () { + var elem = makeEl('div',{itemtype:'token1 token2 token3'}); + elem.itemType.remove('token2'); + assert_equals( elem.itemType.toString(), 'token1 token3' ); +}, 'itemType.remove must collapse whitespace around removed tokens'); +test(function () { + var elem = makeEl('div',{itemtype:' token1 token2 '}); + elem.itemType.remove('token2'); + assert_equals( elem.itemType.toString(), ' token1' ); +}, 'itemType.remove must only remove whitespace around removed tokens'); +test(function () { + var elem = makeEl('div',{itemtype:' token1 token2 token1 '}); + elem.itemType.remove('token2'); + assert_equals( elem.itemType.toString(), ' token1 token1 ' ); +}, 'itemType.remove must collapse multiple whitespace around removed tokens'); +test(function () { + var elem = makeEl('div',{itemtype:' token1 token2 token1 '}); + elem.itemType.remove('token1'); + assert_equals( elem.itemType.toString(), 'token2' ); +}, 'itemType.remove must collapse whitespace when removing multiple tokens'); +test(function () { + var elem = makeEl('div',{itemtype:' token1 token1 '}); + elem.itemType.add('token1'); + assert_equals( elem.itemType.toString(), ' token1 token1 ' ); +}, 'itemType.add must not affect whitespace when the token already exists'); +test(function () { + var elem = makeEl('div',{itemtype:'FOO'}); + assert_true(elem.itemType.toggle('foo')); + assert_equals( elem.itemType.length, 2 ); + assert_true( elem.itemType.contains('foo') ); + assert_true( elem.itemType.contains('FOO') ); +}, 'itemType.toggle must toggle tokens case-sensitively when adding'); +test(function () { + var elem = makeEl('div',{itemtype:'foo FOO'}); + assert_false(elem.itemType.toggle('foo')); + assert_false(elem.itemType.toggle('FOO')); + assert_false( elem.itemType.contains('foo') ); + assert_false( elem.itemType.contains('FOO') ); +}, 'itemType.toggle must be able to remove tokens case-sensitively'); +test(function () { + var elem = makeEl('div',{itemtype:'foo FOO'}); + elem.itemType.toggle('foo'); + elem.itemType.toggle('FOO'); + assert_equals( elem.getAttribute('itemtype'), '' ); +}, 'itemtype attribute must be empty when all classes have been removed'); +test(function () { + var elem = makeEl('div',{itemtype:'foo FOO'}); + elem.itemType.toggle('foo'); + elem.itemType.toggle('FOO'); + assert_equals( elem.itemType.toString(), '' ); +}, 'itemType must stringify to an empty string when all classes have been removed'); +test(function () { + var elem = makeEl('div',{itemtype:'foo FOO'}); + elem.itemType.toggle('foo'); + elem.itemType.toggle('FOO'); + assert_equals( elem.itemType.item(0), null ); +}, 'itemType.item(0) must return null when all classes have been removed'); +test(function () { + /* the normative part of the spec states that: + "unless the length is zero, in which case there are no supported property indices" + ... + "The term[...] supported property indices [is] used as defined in the WebIDL specification." + WebIDL creates actual OwnProperties and then [] just acts as a normal property lookup */ + var elem = makeEl('div',{itemtype:'foo FOO'}); + elem.itemType.toggle('foo'); + elem.itemType.toggle('FOO'); + assert_equals( elem.itemType[0], window.undefined ); +}, 'itemType[0] must be undefined when all classes have been removed'); +//if the last character of DOMTokenSting underlying character is not a space character, append U+0020", where "space character" is from " \t\r\n\f" +test(function () { + var elem = makeEl('div',{itemtype:'a '}); + elem.itemType.add('b'); + assert_equals(elem.itemType.toString(),'a b'); +}, 'itemType.add should treat " " as a space'); +test(function () { + var elem = makeEl('div',{itemtype:'a\t'}); + elem.itemType.add('b'); + assert_equals(elem.itemType.toString(),'a\tb'); +}, 'itemType.add should treat \\t as a space'); +test(function () { + var elem = makeEl('div',{itemtype:'a\r'}); + elem.itemType.add('b'); + assert_equals(elem.itemType.toString(),'a\rb'); +}, 'itemType.add should treat \\r as a space'); +test(function () { + var elem = makeEl('div',{itemtype:'a\n'}); + elem.itemType.add('b'); + assert_equals(elem.itemType.toString(),'a\nb'); +}, 'itemType.add should treat \\n as a space'); +test(function () { + var elem = makeEl('div',{itemtype:'a\f'}); + elem.itemType.add('b'); + assert_equals(elem.itemType.toString(),'a\fb'); +}, 'itemType.add should treat \\f as a space'); +test(function () { + var elem = makeEl('div',{itemtype:'foo'}); + elem.itemType.remove('foo'); + elem.removeAttribute('itemtype'); + assert_true( elem.itemType.toggle('foo') ); +}, 'itemType.toggle must work after removing the itemtype attribute'); +test(function () { + //WebIDL and ECMAScript 5 - a readonly property has a getter but not a setter + //ES5 makes [[Put]] fail but not throw + var failed = false; + var elem = makeEl('div',{itemtype:'token1'}); + try { + elem.itemType.length = 0; + } catch(e) { + failed = e; + } + assert_equals(elem.itemType.length,1); + assert_false(failed,'an error was thrown'); +}, 'itemType.length must be read-only'); +test(function () { + var failed = false, elem = makeEl('div',{itemtype:'test'}), realList = elem.itemType; + try { + elem.itemType = 'dummy'; + } catch(e) { + failed = e; + } + assert_equals(elem.itemType,realList); + assert_equals(elem.itemType.toString(),'dummy','attempting to write should modify the underlying string'); + assert_false(failed,'an error was thrown'); +}, 'itemType must be read-only'); + +/* itemProp property tests (properties collection tests are done later) */ +test(function () { + assert_equals(makeEl('div',{}).itemProp.toString(),'','no attribute'); + assert_equals(makeEl('div',{itemprop:' http://example.com/foo#bar test '}).itemProp.toString(),' http://example.com/foo#bar test ','with URL and simple tokens'); + var testEl = makeEl('div',{itemprop:'http://example.com/foo#bar'}); + testEl.removeAttribute('itemprop'); + assert_equals(testEl.itemProp.toString(),'','removed attribute'); +}, 'the itemprop attribute must be reflected by the .itemProp property'); +test(function () { + assert_equals( typeof makeEl('div',{}).itemProp, 'object' ); +}, 'the itemProp property must be an object'); +test(function () { + assert_true( makeEl('div',{}).itemProp instanceof DOMTokenList, 'instanceof test' ); + DOMTokenList.prototype.customProperty = true; + assert_true( makeEl('div',{}).itemProp.customProperty, 'inheritance test' ); +}, 'the itemProp property must implement DOMTokenList'); +test(function () { + var testEl = makeEl('div',{}); + assert_equals( testEl.itemProp, testEl.itemProp ); +}, 'the itemProp property must always reference the same object'); +test(function () { + assert_equals( makeEl('div',{itemprop:'test test'}).itemProp.length, 2, 'duplicates in initial string should be preserved' ); + assert_equals( makeEl('div',{itemprop:'test test'}).itemProp.item(0), 'test' ); + assert_true( makeEl('div',{itemprop:'test test'}).itemProp.contains('test') ); +}, 'itemProp must be correct for an element that has itemprop tokens'); +test(function () { + assert_equals( makeEl('div',{itemprop:' '}).itemProp.length, 0 ); +}, 'itemProp.length must be 0 for an element that has no tokens'); +test(function () { + assert_false( makeEl('div',{itemprop:' '}).itemProp.contains('foo') ); +}, 'itemProp must not contain an undefined class'); +test(function () { + assert_equals( makeEl('div',{itemprop:' '}).itemProp.item(0), null ); +}, 'itemProp.item() must return null for out-of-range index'); +test(function () { + assert_equals( makeEl('div',{itemprop:' '}).itemProp.item(-1), null ); +}, 'itemProp.item() must return null for negative index'); +test(function () { + /* the normative part of the spec states that: + "unless the length is zero, in which case there are no supported property indices" + ... + "The term[...] supported property indices [is] used as defined in the WebIDL specification." + WebIDL creates actual OwnProperties and then [] just acts as a normal property lookup */ + assert_equals( makeEl('div',{itemprop:' '}).itemProp[0], window.undefined ); +}, 'itemProp[index] must be undefined for out-of-range index'); +test(function () { + assert_equals( makeEl('div',{itemprop:' '}).itemProp[-1], window.undefined ); +}, 'itemProp[index] must be undefined for negative index'); +test(function () { + assert_equals( makeEl('div',{itemprop:' '}).itemProp.toString(), ' ' ); +}, 'empty itemProp should stringify to contain the attribute\'s whitespace'); +test(function () { + assert_throws( 'SYNTAX_ERR', function () { makeEl('div',{itemprop:' '}).itemProp.contains(''); } ); +}, '.contains(empty_string) must throw a SYNTAX_ERR'); +test(function () { + assert_throws( 'SYNTAX_ERR', function () { makeEl('div',{itemprop:' '}).itemProp.add(''); } ); +}, '.add(empty_string) must throw a SYNTAX_ERR'); +test(function () { + assert_throws( 'SYNTAX_ERR', function () { makeEl('div',{itemprop:' '}).itemProp.remove(''); } ); +}, '.remove(empty_string) must throw a SYNTAX_ERR'); +test(function () { + assert_throws( 'SYNTAX_ERR', function () { makeEl('div',{itemprop:' '}).itemProp.toggle(''); } ); +}, '.toggle(empty_string) must throw a SYNTAX_ERR'); +test(function () { + assert_throws( 'INVALID_CHARACTER_ERR', function () { makeEl('div',{itemprop:' '}).itemProp.contains('a b'); } ); +}, '.contains(string_with_spaces) must throw an INVALID_CHARACTER_ERR'); +test(function () { + assert_throws( 'INVALID_CHARACTER_ERR', function () { makeEl('div',{itemprop:' '}).itemProp.add('a b'); } ); +}, '.add(string_with_spaces) must throw an INVALID_CHARACTER_ERR'); +test(function () { + assert_throws( 'INVALID_CHARACTER_ERR', function () { makeEl('div',{itemprop:' '}).itemProp.remove('a b'); } ); +}, '.remove(string_with_spaces) must throw an INVALID_CHARACTER_ERR'); +test(function () { + assert_throws( 'INVALID_CHARACTER_ERR', function () { makeEl('div',{itemprop:' '}).itemProp.toggle('a b'); } ); +}, '.toggle(string_with_spaces) must throw an INVALID_CHARACTER_ERR'); +test(function () { + var testEl = makeEl('div',{itemprop:'foo'}); + assert_true( testEl.itemProp.contains('foo'), 'before change' ); + testEl.setAttribute('itemprop','bar'); + assert_true( testEl.itemProp.contains('bar'), 'after change' ); + assert_false( testEl.itemProp.contains('foo'), 'after change' ); +}, 'itemProp.contains must update when the underlying attribute is changed'); +test(function () { + assert_false( makeEl('div',{itemprop:'foo'}).itemProp.contains('FOO') ); +}, 'itemProp.contains must be case sensitive'); +test(function () { + assert_false( makeEl('div',{itemprop:'foo'}).itemProp.contains('foo.') ); + assert_false( makeEl('div',{itemprop:'foo'}).itemProp.contains('foo)') ); + assert_false( makeEl('div',{itemprop:'foo'}).itemProp.contains('foo\'') ); + assert_false( makeEl('div',{itemprop:'foo'}).itemProp.contains('foo$') ); + assert_false( makeEl('div',{itemprop:'foo'}).itemProp.contains('foo~') ); + assert_false( makeEl('div',{itemprop:'foo'}).itemProp.contains('foo?') ); + assert_false( makeEl('div',{itemprop:'foo'}).itemProp.contains('foo\\') ); +}, 'itemProp.contains must not match when punctuation characters are added'); +test(function () { + var elem = makeEl('div',{itemprop:'foo'}); + elem.itemProp.add('FOO'); + assert_true( elem.itemProp.contains('foo') ); +}, 'itemProp.add must not remove existing tokens'); +test(function () { + assert_true( makeEl('div',{itemprop:'foo FOO'}).itemProp.contains('FOO') ); +}, 'itemProp.contains case sensitivity must match a case-specific string'); +test(function () { + assert_equals( makeEl('div',{itemprop:'foo FOO'}).itemProp.length, 2 ); +}, 'itemProp.length must correctly reflect the number of tokens'); +test(function () { + assert_equals( makeEl('div',{itemprop:'foo FOO'}).itemProp.item(0), 'foo' ); +}, 'itemProp.item(0) must return the first token'); +test(function () { + var elem = makeEl('div',{itemprop:'foo'}); + elem.itemProp.add('FOO'); + assert_equals( elem.itemProp.item(1), 'FOO' ); +}, 'itemProp.item must return case-sensitive strings and preserve token order'); +test(function () { + assert_equals( makeEl('div',{itemprop:'foo FOO'}).itemProp[0], 'foo' ); +}, 'itemProp[0] must return the first token'); +test(function () { + assert_equals( makeEl('div',{itemprop:'foo FOO'}).itemProp[1], 'FOO' ); +}, 'itemProp[index] must return case-sensitive strings and preserve token order'); +test(function () { + /* the normative part of the spec states that: + "unless the length is zero, in which case there are no supported property indices" + ... + "The term[...] supported property indices [is] used as defined in the WebIDL specification." + WebIDL creates actual OwnProperties and then [] just acts as a normal property lookup */ + assert_equals( makeEl('div',{itemprop:'foo FOO'}).itemProp[2], window.undefined ); +}, 'itemProp[index] must still be undefined for out-of-range index when earlier indexes exist'); +test(function () { + var elem = makeEl('div',{}); + elem.itemProp.add('foo'); + elem.itemProp.add('FOO'); + assert_equals( elem.getAttribute('itemprop'), 'foo FOO' ); +}, 'itemprop attribute must update correctly when items have been added through itemProp'); +test(function () { + var elem = makeEl('div',{}); + elem.itemProp.add('foo'); + elem.itemProp.add('FOO'); + assert_equals( elem.itemProp + '', 'foo FOO' ); +}, 'itemProp must stringify correctly when items have been added'); +test(function () { + var elem = makeEl('div',{itemprop:'foo FOO'}); + elem.itemProp.add('foo'); + assert_equals( elem.itemProp.length, 2 ); + assert_equals( elem.itemProp + '', 'foo FOO' ); +}, 'itemProp.add must not make any changes if an existing token is added'); +test(function () { + var elem = makeEl('div',{itemprop:'foo FOO'}); + elem.itemProp.remove('bar'); + assert_equals( elem.itemProp.length, 2 ); + assert_equals( elem.itemProp + '', 'foo FOO' ); +}, 'itemProp.remove must not make any changes if a non-existing token is removed'); +test(function () { + var elem = makeEl('div',{itemprop:'foo FOO'}); + elem.itemProp.remove('foo'); + assert_equals( elem.itemProp.length, 1 ); + assert_equals( elem.itemProp.toString(), 'FOO' ); + assert_false( elem.itemProp.contains('foo') ); + assert_true( elem.itemProp.contains('FOO') ); +}, 'itemProp.remove must remove existing tokens'); +test(function () { + var elem = makeEl('div',{itemprop:'test test'}); + elem.itemProp.remove('test'); + assert_equals( elem.itemProp.length, 0 ); + assert_false( elem.itemProp.contains('test') ); +}, 'itemProp.remove must remove duplicated tokens'); +test(function () { + var elem = makeEl('div',{itemprop:'token1 token2 token3'}); + elem.itemProp.remove('token2'); + assert_equals( elem.itemProp.toString(), 'token1 token3' ); +}, 'itemProp.remove must collapse whitespace around removed tokens'); +test(function () { + var elem = makeEl('div',{itemprop:' token1 token2 '}); + elem.itemProp.remove('token2'); + assert_equals( elem.itemProp.toString(), ' token1' ); +}, 'itemProp.remove must only remove whitespace around removed tokens'); +test(function () { + var elem = makeEl('div',{itemprop:' token1 token2 token1 '}); + elem.itemProp.remove('token2'); + assert_equals( elem.itemProp.toString(), ' token1 token1 ' ); +}, 'itemProp.remove must collapse multiple whitespace around removed tokens'); +test(function () { + var elem = makeEl('div',{itemprop:' token1 token2 token1 '}); + elem.itemProp.remove('token1'); + assert_equals( elem.itemProp.toString(), 'token2' ); +}, 'itemProp.remove must collapse whitespace when removing multiple tokens'); +test(function () { + var elem = makeEl('div',{itemprop:' token1 token1 '}); + elem.itemProp.add('token1'); + assert_equals( elem.itemProp.toString(), ' token1 token1 ' ); +}, 'itemProp.add must not affect whitespace when the token already exists'); +test(function () { + var elem = makeEl('div',{itemprop:'FOO'}); + assert_true(elem.itemProp.toggle('foo')); + assert_equals( elem.itemProp.length, 2 ); + assert_true( elem.itemProp.contains('foo') ); + assert_true( elem.itemProp.contains('FOO') ); +}, 'itemProp.toggle must toggle tokens case-sensitively when adding'); +test(function () { + var elem = makeEl('div',{itemprop:'foo FOO'}); + assert_false(elem.itemProp.toggle('foo')); + assert_false(elem.itemProp.toggle('FOO')); + assert_false( elem.itemProp.contains('foo') ); + assert_false( elem.itemProp.contains('FOO') ); +}, 'itemProp.toggle must be able to remove tokens case-sensitively'); +test(function () { + var elem = makeEl('div',{itemprop:'foo FOO'}); + elem.itemProp.toggle('foo'); + elem.itemProp.toggle('FOO'); + assert_equals( elem.getAttribute('itemprop'), '' ); +}, 'itemprop attribute must be empty when all classes have been removed'); +test(function () { + var elem = makeEl('div',{itemprop:'foo FOO'}); + elem.itemProp.toggle('foo'); + elem.itemProp.toggle('FOO'); + assert_equals( elem.itemProp.toString(), '' ); +}, 'itemProp must stringify to an empty string when all classes have been removed'); +test(function () { + var elem = makeEl('div',{itemprop:'foo FOO'}); + elem.itemProp.toggle('foo'); + elem.itemProp.toggle('FOO'); + assert_equals( elem.itemProp.item(0), null ); +}, 'itemProp.item(0) must return null when all classes have been removed'); +test(function () { + /* the normative part of the spec states that: + "unless the length is zero, in which case there are no supported property indices" + ... + "The term[...] supported property indices [is] used as defined in the WebIDL specification." + WebIDL creates actual OwnProperties and then [] just acts as a normal property lookup */ + var elem = makeEl('div',{itemprop:'foo FOO'}); + elem.itemProp.toggle('foo'); + elem.itemProp.toggle('FOO'); + assert_equals( elem.itemProp[0], window.undefined ); +}, 'itemProp[0] must be undefined when all classes have been removed'); +//if the last character of DOMTokenSting underlying character is not a space character, append U+0020", where "space character" is from " \t\r\n\f" +test(function () { + var elem = makeEl('div',{itemprop:'a '}); + elem.itemProp.add('b'); + assert_equals(elem.itemProp.toString(),'a b'); +}, 'itemProp.add should treat " " as a space'); +test(function () { + var elem = makeEl('div',{itemprop:'a\t'}); + elem.itemProp.add('b'); + assert_equals(elem.itemProp.toString(),'a\tb'); +}, 'itemProp.add should treat \\t as a space'); +test(function () { + var elem = makeEl('div',{itemprop:'a\r'}); + elem.itemProp.add('b'); + assert_equals(elem.itemProp.toString(),'a\rb'); +}, 'itemProp.add should treat \\r as a space'); +test(function () { + var elem = makeEl('div',{itemprop:'a\n'}); + elem.itemProp.add('b'); + assert_equals(elem.itemProp.toString(),'a\nb'); +}, 'itemProp.add should treat \\n as a space'); +test(function () { + var elem = makeEl('div',{itemprop:'a\f'}); + elem.itemProp.add('b'); + assert_equals(elem.itemProp.toString(),'a\fb'); +}, 'itemProp.add should treat \\f as a space'); +test(function () { + var elem = makeEl('div',{itemprop:'foo'}); + elem.itemProp.remove('foo'); + elem.removeAttribute('itemprop'); + assert_true( elem.itemProp.toggle('foo') ); +}, 'itemProp.toggle must work after removing the itemprop attribute'); +test(function () { + //WebIDL and ECMAScript 5 - a readonly property has a getter but not a setter + //ES5 makes [[Put]] fail but not throw + var failed = false; + var elem = makeEl('div',{itemprop:'token1'}); + try { + elem.itemProp.length = 0; + } catch(e) { + failed = e; + } + assert_equals(elem.itemProp.length,1); + assert_false(failed,'an error was thrown'); +}, 'itemProp.length must be read-only'); +test(function () { + var failed = false, elem = makeEl('div',{itemprop:'test'}), realList = elem.itemProp; + try { + elem.itemProp = 'dummy'; + } catch(e) { + failed = e; + } + assert_equals(elem.itemProp,realList); + assert_equals(elem.itemProp.toString(),'dummy','attempting to write should modify the underlying string'); + assert_false(failed,'an error was thrown'); +}, 'itemProp must be read-only'); + +/* itemId property tests */ +test(function () { + assert_equals( makeEl('div',{itemid:'http://example.com/foo'}).itemId, 'http://example.com/foo' ); + assert_equals( makeEl('div',{itemid:'http://example.com/FOO'}).itemId, 'http://example.com/FOO', 'case-sensitive' ); + assert_equals( makeEl('div',{itemid:' http://example.com/foo '}).itemId, 'http://example.com/foo', 'whitespace' ); + assert_equals( makeEl('div',{itemid:'data:text/plain,'}).itemId, 'data:text/plain,' ); + assert_equals( makeEl('div',{itemid:'madeup:onthespot'}).itemId, 'madeup:onthespot' ); + assert_equals( makeEl('div',{}).itemId, '' ); +}, 'the itemid attribute must be reflected by the .itemId property'); +test(function () { + var testEl = makeEl('div',{}); + testEl.itemId = 'http://example.com/foo'; + assert_equals(testEl.itemId,'http://example.com/foo','writing a URL'); + testEl.itemId = ''; + assert_equals(testEl.itemId,location.href,'writing an empty string'); +}, 'the itemId property must be read/write'); +test(function () { + var testEl = makeEl('div',{}); + testEl.itemId = 'http://example.com/foo'; + assert_true(testEl.hasAttribute('itemid'),'writing a URL'); + assert_equals(testEl.getAttribute('itemid'),'http://example.com/foo','writing a URL'); + testEl = makeEl('div',{}) + testEl.itemId = ''; + assert_true(testEl.hasAttribute('itemid'),'writing an empty string'); + assert_equals(testEl.getAttribute('itemid'),'','writing an empty string'); +}, 'writing to the itemId property must create the itemid content attribute'); +test(function () { + assert_equals( makeEl('div',{itemid:'foo'}).itemId, location.href.replace(/\/[^\/]*$/,'\/foo'),'foo' ); + assert_equals( makeEl('div',{itemid:'foo bar'}).itemId, location.href.replace(/\/[^\/]*$/,'\/foo%20bar'),'foo bar' ); + assert_equals( makeEl('div',{itemid:'foo\u0129 bar'}).itemId, location.href.replace(/\/[^\/]*$/,'\/foo%C4%A9%20bar'),'foo\u0129 bar' ); +}, 'the itemId property must see the resolved itemid URL'); +test(function () { + var testEl = makeEl('div',{}); + testEl.itemId = 'foo'; + assert_equals( testEl.itemId, location.href.replace(/\/[^\/]*$/,'\/foo') ); +}, 'the itemId property must see the resolved itemId property URL on setting'); +test(function () { + var testEl = makeEl('div',{}); + testEl.itemId = 'foo'; + assert_equals( testEl.getAttribute('itemid'), 'foo' ); +}, 'the itemid attribute must see the resolved itemId URL'); + +/* itemRef property tests (properties collection tests are done later) */ +test(function () { + assert_equals(makeEl('div',{}).itemRef.toString(),'','no attribute'); + assert_equals(makeEl('div',{itemref:' foo bar '}).itemRef.toString(),' foo bar ','with simple tokens'); + var testEl = makeEl('div',{itemref:'foo'}); + testEl.removeAttribute('itemref'); + assert_equals(testEl.itemRef.toString(),'','removed attribute'); +}, 'the itemref attribute must be reflected by the .itemRef property'); +test(function () { + assert_equals( typeof makeEl('div',{}).itemRef, 'object' ); +}, 'the itemRef property must be an object'); +test(function () { + assert_true( makeEl('div',{}).itemRef instanceof DOMTokenList, 'instanceof test' ); + DOMTokenList.prototype.customProperty = true; + assert_true( makeEl('div',{}).itemRef.customProperty, 'inheritance test' ); +}, 'the itemRef property must implement DOMTokenList'); +test(function () { + var testEl = makeEl('div',{}); + assert_equals( testEl.itemRef, testEl.itemRef ); +}, 'the itemRef property must always reference the same object'); +test(function () { + assert_equals( makeEl('div',{itemref:'test test'}).itemRef.length, 2, 'duplicates in initial string should be preserved' ); + assert_equals( makeEl('div',{itemref:'test test'}).itemRef.item(0), 'test' ); + assert_true( makeEl('div',{itemref:'test test'}).itemRef.contains('test') ); +}, 'itemRef must be correct for an element that has itemref tokens'); +test(function () { + assert_equals( makeEl('div',{itemref:' '}).itemRef.length, 0 ); +}, 'itemRef.length must be 0 for an element that has no tokens'); +test(function () { + assert_false( makeEl('div',{itemref:' '}).itemRef.contains('foo') ); +}, 'itemRef must not contain an undefined class'); +test(function () { + assert_equals( makeEl('div',{itemref:' '}).itemRef.item(0), null ); +}, 'itemRef.item() must return null for out-of-range index'); +test(function () { + assert_equals( makeEl('div',{itemref:' '}).itemRef.item(-1), null ); +}, 'itemRef.item() must return null for negative index'); +test(function () { + /* the normative part of the spec states that: + "unless the length is zero, in which case there are no supported property indices" + ... + "The term[...] supported property indices [is] used as defined in the WebIDL specification." + WebIDL creates actual OwnProperties and then [] just acts as a normal property lookup */ + assert_equals( makeEl('div',{itemref:' '}).itemRef[0], window.undefined ); +}, 'itemRef[index] must be undefined for out-of-range index'); +test(function () { + assert_equals( makeEl('div',{itemref:' '}).itemRef[-1], window.undefined ); +}, 'itemRef[index] must be undefined for negative index'); +test(function () { + assert_equals( makeEl('div',{itemref:' '}).itemRef.toString(), ' ' ); +}, 'empty itemRef should stringify to contain the attribute\'s whitespace'); +test(function () { + assert_throws( 'SYNTAX_ERR', function () { makeEl('div',{itemref:' '}).itemRef.contains(''); } ); +}, '.contains(empty_string) must throw a SYNTAX_ERR'); +test(function () { + assert_throws( 'SYNTAX_ERR', function () { makeEl('div',{itemref:' '}).itemRef.add(''); } ); +}, '.add(empty_string) must throw a SYNTAX_ERR'); +test(function () { + assert_throws( 'SYNTAX_ERR', function () { makeEl('div',{itemref:' '}).itemRef.remove(''); } ); +}, '.remove(empty_string) must throw a SYNTAX_ERR'); +test(function () { + assert_throws( 'SYNTAX_ERR', function () { makeEl('div',{itemref:' '}).itemRef.toggle(''); } ); +}, '.toggle(empty_string) must throw a SYNTAX_ERR'); +test(function () { + assert_throws( 'INVALID_CHARACTER_ERR', function () { makeEl('div',{itemref:' '}).itemRef.contains('a b'); } ); +}, '.contains(string_with_spaces) must throw an INVALID_CHARACTER_ERR'); +test(function () { + assert_throws( 'INVALID_CHARACTER_ERR', function () { makeEl('div',{itemref:' '}).itemRef.add('a b'); } ); +}, '.add(string_with_spaces) must throw an INVALID_CHARACTER_ERR'); +test(function () { + assert_throws( 'INVALID_CHARACTER_ERR', function () { makeEl('div',{itemref:' '}).itemRef.remove('a b'); } ); +}, '.remove(string_with_spaces) must throw an INVALID_CHARACTER_ERR'); +test(function () { + assert_throws( 'INVALID_CHARACTER_ERR', function () { makeEl('div',{itemref:' '}).itemRef.toggle('a b'); } ); +}, '.toggle(string_with_spaces) must throw an INVALID_CHARACTER_ERR'); +test(function () { + var testEl = makeEl('div',{itemref:'foo'}); + assert_true( testEl.itemRef.contains('foo'), 'before change' ); + testEl.setAttribute('itemref','bar'); + assert_true( testEl.itemRef.contains('bar'), 'after change' ); + assert_false( testEl.itemRef.contains('foo'), 'after change' ); +}, 'itemRef.contains must update when the underlying attribute is changed'); +test(function () { + assert_false( makeEl('div',{itemref:'foo'}).itemRef.contains('FOO') ); +}, 'itemRef.contains must be case sensitive'); +test(function () { + assert_false( makeEl('div',{itemref:'foo'}).itemRef.contains('foo.') ); + assert_false( makeEl('div',{itemref:'foo'}).itemRef.contains('foo)') ); + assert_false( makeEl('div',{itemref:'foo'}).itemRef.contains('foo\'') ); + assert_false( makeEl('div',{itemref:'foo'}).itemRef.contains('foo$') ); + assert_false( makeEl('div',{itemref:'foo'}).itemRef.contains('foo~') ); + assert_false( makeEl('div',{itemref:'foo'}).itemRef.contains('foo?') ); + assert_false( makeEl('div',{itemref:'foo'}).itemRef.contains('foo\\') ); +}, 'itemRef.contains must not match when punctuation characters are added'); +test(function () { + var elem = makeEl('div',{itemref:'foo'}); + elem.itemRef.add('FOO'); + assert_true( elem.itemRef.contains('foo') ); +}, 'itemRef.add must not remove existing tokens'); +test(function () { + assert_true( makeEl('div',{itemref:'foo FOO'}).itemRef.contains('FOO') ); +}, 'itemRef.contains case sensitivity must match a case-specific string'); +test(function () { + assert_equals( makeEl('div',{itemref:'foo FOO'}).itemRef.length, 2 ); +}, 'itemRef.length must correctly reflect the number of tokens'); +test(function () { + assert_equals( makeEl('div',{itemref:'foo FOO'}).itemRef.item(0), 'foo' ); +}, 'itemRef.item(0) must return the first token'); +test(function () { + var elem = makeEl('div',{itemref:'foo'}); + elem.itemRef.add('FOO'); + assert_equals( elem.itemRef.item(1), 'FOO' ); +}, 'itemRef.item must return case-sensitive strings and preserve token order'); +test(function () { + assert_equals( makeEl('div',{itemref:'foo FOO'}).itemRef[0], 'foo' ); +}, 'itemRef[0] must return the first token'); +test(function () { + assert_equals( makeEl('div',{itemref:'foo FOO'}).itemRef[1], 'FOO' ); +}, 'itemRef[index] must return case-sensitive strings and preserve token order'); +test(function () { + /* the normative part of the spec states that: + "unless the length is zero, in which case there are no supported property indices" + ... + "The term[...] supported property indices [is] used as defined in the WebIDL specification." + WebIDL creates actual OwnProperties and then [] just acts as a normal property lookup */ + assert_equals( makeEl('div',{itemref:'foo FOO'}).itemRef[2], window.undefined ); +}, 'itemRef[index] must still be undefined for out-of-range index when earlier indexes exist'); +test(function () { + var elem = makeEl('div',{}); + elem.itemRef.add('foo'); + elem.itemRef.add('FOO'); + assert_equals( elem.getAttribute('itemref'), 'foo FOO' ); +}, 'itemref attribute must update correctly when items have been added through itemRef'); +test(function () { + var elem = makeEl('div',{}); + elem.itemRef.add('foo'); + elem.itemRef.add('FOO'); + assert_equals( elem.itemRef + '', 'foo FOO' ); +}, 'itemRef must stringify correctly when items have been added'); +test(function () { + var elem = makeEl('div',{itemref:'foo FOO'}); + elem.itemRef.add('foo'); + assert_equals( elem.itemRef.length, 2 ); + assert_equals( elem.itemRef + '', 'foo FOO' ); +}, 'itemRef.add must not make any changes if an existing token is added'); +test(function () { + var elem = makeEl('div',{itemref:'foo FOO'}); + elem.itemRef.remove('bar'); + assert_equals( elem.itemRef.length, 2 ); + assert_equals( elem.itemRef + '', 'foo FOO' ); +}, 'itemRef.remove must not make any changes if a non-existing token is removed'); +test(function () { + var elem = makeEl('div',{itemref:'foo FOO'}); + elem.itemRef.remove('foo'); + assert_equals( elem.itemRef.length, 1 ); + assert_equals( elem.itemRef.toString(), 'FOO' ); + assert_false( elem.itemRef.contains('foo') ); + assert_true( elem.itemRef.contains('FOO') ); +}, 'itemRef.remove must remove existing tokens'); +test(function () { + var elem = makeEl('div',{itemref:'test test'}); + elem.itemRef.remove('test'); + assert_equals( elem.itemRef.length, 0 ); + assert_false( elem.itemRef.contains('test') ); +}, 'itemRef.remove must remove duplicated tokens'); +test(function () { + var elem = makeEl('div',{itemref:'token1 token2 token3'}); + elem.itemRef.remove('token2'); + assert_equals( elem.itemRef.toString(), 'token1 token3' ); +}, 'itemRef.remove must collapse whitespace around removed tokens'); +test(function () { + var elem = makeEl('div',{itemref:' token1 token2 '}); + elem.itemRef.remove('token2'); + assert_equals( elem.itemRef.toString(), ' token1' ); +}, 'itemRef.remove must only remove whitespace around removed tokens'); +test(function () { + var elem = makeEl('div',{itemref:' token1 token2 token1 '}); + elem.itemRef.remove('token2'); + assert_equals( elem.itemRef.toString(), ' token1 token1 ' ); +}, 'itemRef.remove must collapse multiple whitespace around removed tokens'); +test(function () { + var elem = makeEl('div',{itemref:' token1 token2 token1 '}); + elem.itemRef.remove('token1'); + assert_equals( elem.itemRef.toString(), 'token2' ); +}, 'itemRef.remove must collapse whitespace when removing multiple tokens'); +test(function () { + var elem = makeEl('div',{itemref:' token1 token1 '}); + elem.itemRef.add('token1'); + assert_equals( elem.itemRef.toString(), ' token1 token1 ' ); +}, 'itemRef.add must not affect whitespace when the token already exists'); +test(function () { + var elem = makeEl('div',{itemref:'FOO'}); + assert_true(elem.itemRef.toggle('foo')); + assert_equals( elem.itemRef.length, 2 ); + assert_true( elem.itemRef.contains('foo') ); + assert_true( elem.itemRef.contains('FOO') ); +}, 'itemRef.toggle must toggle tokens case-sensitively when adding'); +test(function () { + var elem = makeEl('div',{itemref:'foo FOO'}); + assert_false(elem.itemRef.toggle('foo')); + assert_false(elem.itemRef.toggle('FOO')); + assert_false( elem.itemRef.contains('foo') ); + assert_false( elem.itemRef.contains('FOO') ); +}, 'itemRef.toggle must be able to remove tokens case-sensitively'); +test(function () { + var elem = makeEl('div',{itemref:'foo FOO'}); + elem.itemRef.toggle('foo'); + elem.itemRef.toggle('FOO'); + assert_equals( elem.getAttribute('itemref'), '' ); +}, 'itemref attribute must be empty when all classes have been removed'); +test(function () { + var elem = makeEl('div',{itemref:'foo FOO'}); + elem.itemRef.toggle('foo'); + elem.itemRef.toggle('FOO'); + assert_equals( elem.itemRef.toString(), '' ); +}, 'itemRef must stringify to an empty string when all classes have been removed'); +test(function () { + var elem = makeEl('div',{itemref:'foo FOO'}); + elem.itemRef.toggle('foo'); + elem.itemRef.toggle('FOO'); + assert_equals( elem.itemRef.item(0), null ); +}, 'itemRef.item(0) must return null when all classes have been removed'); +test(function () { + /* the normative part of the spec states that: + "unless the length is zero, in which case there are no supported property indices" + ... + "The term[...] supported property indices [is] used as defined in the WebIDL specification." + WebIDL creates actual OwnProperties and then [] just acts as a normal property lookup */ + var elem = makeEl('div',{itemref:'foo FOO'}); + elem.itemRef.toggle('foo'); + elem.itemRef.toggle('FOO'); + assert_equals( elem.itemRef[0], window.undefined ); +}, 'itemRef[0] must be undefined when all classes have been removed'); +//if the last character of DOMTokenSting underlying character is not a space character, append U+0020", where "space character" is from " \t\r\n\f" +test(function () { + var elem = makeEl('div',{itemref:'a '}); + elem.itemRef.add('b'); + assert_equals(elem.itemRef.toString(),'a b'); +}, 'itemRef.add should treat " " as a space'); +test(function () { + var elem = makeEl('div',{itemref:'a\t'}); + elem.itemRef.add('b'); + assert_equals(elem.itemRef.toString(),'a\tb'); +}, 'itemRef.add should treat \\t as a space'); +test(function () { + var elem = makeEl('div',{itemref:'a\r'}); + elem.itemRef.add('b'); + assert_equals(elem.itemRef.toString(),'a\rb'); +}, 'itemRef.add should treat \\r as a space'); +test(function () { + var elem = makeEl('div',{itemref:'a\n'}); + elem.itemRef.add('b'); + assert_equals(elem.itemRef.toString(),'a\nb'); +}, 'itemRef.add should treat \\n as a space'); +test(function () { + var elem = makeEl('div',{itemref:'a\f'}); + elem.itemRef.add('b'); + assert_equals(elem.itemRef.toString(),'a\fb'); +}, 'itemRef.add should treat \\f as a space'); +test(function () { + var elem = makeEl('div',{itemref:'foo'}); + elem.itemRef.remove('foo'); + elem.removeAttribute('itemref'); + assert_true( elem.itemRef.toggle('foo') ); +}, 'itemRef.toggle must work after removing the itemref attribute'); +test(function () { + //WebIDL and ECMAScript 5 - a readonly property has a getter but not a setter + //ES5 makes [[Put]] fail but not throw + var failed = false; + var elem = makeEl('div',{itemref:'token1'}); + try { + elem.itemRef.length = 0; + } catch(e) { + failed = e; + } + assert_equals(elem.itemRef.length,1); + assert_false(failed,'an error was thrown'); +}, 'itemRef.length must be read-only'); +test(function () { + var failed = false, elem = makeEl('div',{itemref:'test'}), realList = elem.itemRef; + try { + elem.itemRef = 'dummy'; + } catch(e) { + failed = e; + } + assert_equals(elem.itemRef,realList); + assert_equals(elem.itemRef.toString(),'dummy','attempting to write should modify the underlying string'); + assert_false(failed,'an error was thrown'); +}, 'itemRef must be read-only'); + +/* itemValue property tests */ +test(function () { + assert_equals( makeEl('meta',{content:'test'}).itemValue, null, 'meta' ); + assert_equals( makeEl('audio',{src:'test'}).itemValue, null, 'audio' ); + assert_equals( makeEl('embed',{src:'test'}).itemValue, null, 'embed' ); + assert_equals( makeEl('iframe',{src:'test'}).itemValue, null, 'iframe' ); + assert_equals( makeEl('img',{src:'test'}).itemValue, null, 'img' ); + assert_equals( makeEl('source',{src:'test'}).itemValue, null, 'source' ); + assert_equals( makeEl('track',{src:'test'}).itemValue, null, 'track' ); + assert_equals( makeEl('video',{src:'test'}).itemValue, null, 'video' ); + assert_equals( makeEl('a',{href:'test'}).itemValue, null, 'a' ); + assert_equals( makeEl('area',{href:'test'}).itemValue, null, 'area' ); + assert_equals( makeEl('link',{href:'test'}).itemValue, null, 'link' ); + assert_equals( makeEl('object',{data:'test'}).itemValue, null, 'object' ); + assert_equals( makeEl('time',{}).itemValue, null, 'time without datetime' ); + assert_equals( makeEl('time',{datetime:'test'}).itemValue, null, 'time with datetime' ); + assert_equals( makeEl('div',{},'test').itemValue, null, 'otherwise' ); + assert_equals( makeEl('madeuponthespot',{},'test').itemValue, null, 'unknown element' ); +}, 'itemValue must be null if the element does not have an itemprop attribute'); +test(function () { + assert_throws( 'INVALID_ACCESS_ERR', function () { var testEl = makeEl('meta',{content:'test'}); testEl.itemValue = 'test2'; }, 'meta' ); + assert_throws( 'INVALID_ACCESS_ERR', function () { var testEl = makeEl('audio',{content:'test'}); testEl.itemValue = 'test2'; }, 'audio' ); + assert_throws( 'INVALID_ACCESS_ERR', function () { var testEl = makeEl('embed',{content:'test'}); testEl.itemValue = 'test2'; }, 'embed' ); + assert_throws( 'INVALID_ACCESS_ERR', function () { var testEl = makeEl('iframe',{content:'test'}); testEl.itemValue = 'test2'; }, 'iframe' ); + assert_throws( 'INVALID_ACCESS_ERR', function () { var testEl = makeEl('img',{content:'test'}); testEl.itemValue = 'test2'; }, 'img' ); + assert_throws( 'INVALID_ACCESS_ERR', function () { var testEl = makeEl('source',{content:'test'}); testEl.itemValue = 'test2'; }, 'source' ); + assert_throws( 'INVALID_ACCESS_ERR', function () { var testEl = makeEl('track',{content:'test'}); testEl.itemValue = 'test2'; }, 'track' ); + assert_throws( 'INVALID_ACCESS_ERR', function () { var testEl = makeEl('video',{content:'test'}); testEl.itemValue = 'test2'; }, 'video' ); + assert_throws( 'INVALID_ACCESS_ERR', function () { var testEl = makeEl('a',{href:'test'}); testEl.itemValue = 'test2'; }, 'a' ); + assert_throws( 'INVALID_ACCESS_ERR', function () { var testEl = makeEl('area',{href:'test'}); testEl.itemValue = 'test2'; }, 'area' ); + assert_throws( 'INVALID_ACCESS_ERR', function () { var testEl = makeEl('link',{href:'test'}); testEl.itemValue = 'test2'; }, 'link' ); + assert_throws( 'INVALID_ACCESS_ERR', function () { var testEl = makeEl('object',{data:'test'}); testEl.itemValue = 'test2'; }, 'object' ); + assert_throws( 'INVALID_ACCESS_ERR', function () { var testEl = makeEl('time',{}); testEl.itemValue = 'test2'; }, 'time without datetime' ); + assert_throws( 'INVALID_ACCESS_ERR', function () { var testEl = makeEl('time',{datetime:'test'}); testEl.itemValue = 'test2'; }, 'time with datetime' ); + assert_throws( 'INVALID_ACCESS_ERR', function () { var testEl = makeEl('div',{},'test'); testEl.itemValue = 'test2'; }, 'otherwise' ); + assert_throws( 'INVALID_ACCESS_ERR', function () { var testEl = makeEl('madeuponthespot',{},'test'); testEl.itemValue = 'test2'; }, 'unknown element' ); +}, 'writing to itemValue must throw an INVALID_ACCESS_ERR error if the element does not have an itemprop attribute'); +test(function () { + var testEl; + testEl = makeEl('meta',{itemscope:'itemscope',itemprop:'foo',content:'test'}); + assert_equals( testEl.itemValue, testEl, 'meta' ); + testEl = makeEl('audio',{itemscope:'itemscope',itemprop:'foo',src:'test'},'fail'); + assert_equals( testEl.itemValue, testEl, 'audio' ); + testEl = makeEl('embed',{itemscope:'itemscope',itemprop:'foo',src:'test'}); + assert_equals( testEl.itemValue, testEl, 'embed' ); + testEl = makeEl('iframe',{itemscope:'itemscope',itemprop:'foo',src:'test'},'fail'); + assert_equals( testEl.itemValue, testEl, 'iframe' ); + testEl = makeEl('img',{itemscope:'itemscope',itemprop:'foo',src:'test'}); + assert_equals( testEl.itemValue, testEl, 'img' ); + testEl = makeEl('source',{itemscope:'itemscope',itemprop:'foo',src:'test'}); + assert_equals( testEl.itemValue, testEl, 'source' ); + testEl = makeEl('track',{itemscope:'itemscope',itemprop:'foo',src:'test'}); + assert_equals( testEl.itemValue, testEl, 'track' ); + testEl = makeEl('video',{itemscope:'itemscope',itemprop:'foo',src:'test'},'fail'); + assert_equals( testEl.itemValue, testEl, 'video' ); + testEl = makeEl('a',{itemscope:'itemscope',itemprop:'foo',href:'test'},'fail'); + assert_equals( testEl.itemValue, testEl, 'a' ); + testEl = makeEl('area',{itemscope:'itemscope',itemprop:'foo',href:'test'}); + assert_equals( testEl.itemValue, testEl, 'area' ); + testEl = makeEl('link',{itemscope:'itemscope',itemprop:'foo',href:'test'}); + assert_equals( testEl.itemValue, testEl, 'link' ); + testEl = makeEl('object',{itemscope:'itemscope',itemprop:'foo',data:'test'},'fail'); + assert_equals( testEl.itemValue, testEl, 'object' ); + testEl = makeEl('time',{itemscope:'itemscope',itemprop:'foo'},'fail'); + assert_equals( testEl.itemValue, testEl, 'time without datetime' ); + testEl = makeEl('time',{itemscope:'itemscope',itemprop:'foo',datetime:'test'},'fail'); + assert_equals( testEl.itemValue, testEl, 'time with datetime' ); + testEl = makeEl('div',{itemscope:'itemscope',itemprop:'foo'},'test'); + assert_equals( testEl.itemValue, testEl, 'otherwise' ); + testEl = makeEl('madeuponthespot',{itemscope:'itemscope',itemprop:'foo'},'test'); + assert_equals( testEl.itemValue, testEl, 'unknown element' ); + testEl = makeEl('madeuponthespot',{itemscope:'itemscope',itemprop:'foo',content:'test',src:'test',href:'test',data:'test',datetime:'test',value:'test'},'test'); + assert_equals( testEl.itemValue, testEl, 'unknown element with known attributes' ); + testEl = makeEl('input',{itemscope:'itemscope',itemprop:'foo',value:'test'},'test'); + assert_equals( testEl.itemValue, testEl, 'input' ); +}, 'itemValue must return the element if the element has an itemscope attribute'); +test(function () { + var testEl = makeEl('meta',{itemprop:'foo',content:'test'}); + assert_equals( testEl.itemValue, 'test', 'reading' ); + testEl.content = 'retest'; + assert_equals( testEl.itemValue, 'retest', 'reading after change' ); + testEl.itemValue = 'bar'; + assert_equals( testEl.content, 'bar', 'writing (checking content)' ); + assert_equals( testEl.textContent, '', 'writing (checking textContent)' ); +}, 'itemValue must reflect the content attribute on meta elements'); +test(function () { + var testEl = makeEl('audio',{itemprop:'foo',src:'http://example.org/'},'contained text'); + assert_equals( testEl.itemValue, 'http://example.org/', 'reading' ); + testEl.src = 'http://example.net/'; + assert_equals( testEl.itemValue, 'http://example.net/', 'reading after change' ); + testEl.itemValue = 'http://example.com/'; + assert_equals( testEl.src, 'http://example.com/', 'writing (checking src)' ); + assert_equals( testEl.textContent, 'contained text', 'writing (checking textContent)' ); + assert_equals( makeEl('audio',{itemprop:'foo'},'contained text').itemValue, '', 'reading with missing attribute' ); + testEl.src = 'bar'; + assert_equals( testEl.itemValue, location.href.replace(/\/[^\/]*$/,'/bar'), 'resolving URLs' ); +}, 'itemValue must reflect the src attribute on audio elements'); +test(function () { + var testEl = makeEl('embed',{itemprop:'foo',src:'http://example.org/'}); + assert_equals( testEl.itemValue, 'http://example.org/', 'reading' ); + testEl.src = 'http://example.net/'; + assert_equals( testEl.itemValue, 'http://example.net/', 'reading after change' ); + testEl.itemValue = 'http://example.com/'; + assert_equals( testEl.src, 'http://example.com/', 'writing (checking src)' ); + assert_equals( testEl.textContent, '', 'writing (checking textContent)' ); + testEl.src = 'bar'; + assert_equals( testEl.itemValue, location.href.replace(/\/[^\/]*$/,'/bar'), 'resolving URLs' ); +}, 'itemValue must reflect the src attribute on embed elements'); +test(function () { + var testEl = makeEl('iframe',{itemprop:'foo',src:'http://example.org/'},'contained text'); + assert_equals( testEl.itemValue, 'http://example.org/', 'reading' ); + testEl.src = 'http://example.net/'; + assert_equals( testEl.itemValue, 'http://example.net/', 'reading after change' ); + testEl.itemValue = 'http://example.com/'; + assert_equals( testEl.src, 'http://example.com/', 'writing (checking src)' ); + assert_equals( testEl.textContent, 'contained text', 'writing (checking textContent)' ); + assert_equals( makeEl('iframe',{itemprop:'foo'},'contained text').itemValue, '', 'reading with missing attribute' ); + testEl.src = 'bar'; + assert_equals( testEl.itemValue, location.href.replace(/\/[^\/]*$/,'/bar'), 'resolving URLs' ); +}, 'itemValue must reflect the src attribute on iframe elements'); +test(function () { + var testEl = makeEl('img',{itemprop:'foo',src:'http://example.org/'}); + assert_equals( testEl.itemValue, 'http://example.org/', 'reading' ); + testEl.src = 'http://example.net/'; + assert_equals( testEl.itemValue, 'http://example.net/', 'reading after change' ); + testEl.itemValue = 'http://example.com/'; + assert_equals( testEl.src, 'http://example.com/', 'writing (checking src)' ); + assert_equals( testEl.textContent, '', 'writing (checking textContent)' ); + testEl.src = 'bar'; + assert_equals( testEl.itemValue, location.href.replace(/\/[^\/]*$/,'/bar'), 'resolving URLs' ); +}, 'itemValue must reflect the src attribute on img elements'); +test(function () { + var testEl = makeEl('source',{itemprop:'foo',src:'http://example.org/'}); + assert_equals( testEl.itemValue, 'http://example.org/', 'reading' ); + testEl.src = 'http://example.net/'; + assert_equals( testEl.itemValue, 'http://example.net/', 'reading after change' ); + testEl.itemValue = 'http://example.com/'; + assert_equals( testEl.src, 'http://example.com/', 'writing (checking src)' ); + assert_equals( testEl.textContent, '', 'writing (checking textContent)' ); + testEl.src = 'bar'; + assert_equals( testEl.itemValue, location.href.replace(/\/[^\/]*$/,'/bar'), 'resolving URLs' ); +}, 'itemValue must reflect the src attribute on source elements'); +test(function () { + var testEl = makeEl('track',{itemprop:'foo',src:'http://example.org/'}); + assert_equals( testEl.itemValue, 'http://example.org/', 'reading' ); + testEl.src = 'http://example.net/'; + assert_equals( testEl.itemValue, 'http://example.net/', 'reading after change' ); + testEl.itemValue = 'http://example.com/'; + assert_equals( testEl.src, 'http://example.com/', 'writing (checking src)' ); + assert_equals( testEl.textContent, '', 'writing (checking textContent)' ); + testEl.src = 'bar'; + assert_equals( testEl.itemValue, location.href.replace(/\/[^\/]*$/,'/bar'), 'resolving URLs' ); +}, 'itemValue must reflect the src attribute on track elements'); +test(function () { + var testEl = makeEl('video',{itemprop:'foo',src:'http://example.org/'},'contained text'); + assert_equals( testEl.itemValue, 'http://example.org/', 'reading' ); + testEl.src = 'http://example.net/'; + assert_equals( testEl.itemValue, 'http://example.net/', 'reading after change' ); + testEl.itemValue = 'http://example.com/'; + assert_equals( testEl.src, 'http://example.com/', 'writing (checking src)' ); + assert_equals( testEl.textContent, 'contained text', 'writing (checking textContent)' ); + assert_equals( makeEl('video',{itemprop:'foo'},'contained text').itemValue, '', 'reading with missing attribute' ); + testEl.src = 'bar'; + assert_equals( testEl.itemValue, location.href.replace(/\/[^\/]*$/,'/bar'), 'resolving URLs' ); +}, 'itemValue must reflect the src attribute on video elements'); +test(function () { + var testEl = makeEl('a',{itemprop:'foo',href:'http://example.org/'},'contained text'); + assert_equals( testEl.itemValue, 'http://example.org/', 'reading' ); + testEl.href = 'http://example.net/'; + assert_equals( testEl.itemValue, 'http://example.net/', 'reading after change' ); + testEl.itemValue = 'http://example.com/'; + assert_equals( testEl.href, 'http://example.com/', 'writing (checking href)' ); + assert_equals( testEl.textContent, 'contained text', 'writing (checking textContent)' ); + assert_equals( makeEl('a',{itemprop:'foo'},'contained text').itemValue, '', 'reading with missing attribute' ); + testEl.href = 'bar'; + assert_equals( testEl.itemValue, location.href.replace(/\/[^\/]*$/,'/bar'), 'resolving URLs' ); +}, 'itemValue must reflect the src attribute on anchor elements'); +test(function () { + var testEl = makeEl('area',{itemprop:'foo',href:'http://example.org/'}); + assert_equals( testEl.itemValue, 'http://example.org/', 'reading' ); + testEl.href = 'http://example.net/'; + assert_equals( testEl.itemValue, 'http://example.net/', 'reading after change' ); + testEl.itemValue = 'http://example.com/'; + assert_equals( testEl.href, 'http://example.com/', 'writing (checking href)' ); + assert_equals( testEl.textContent, '', 'writing (checking textContent)' ); + testEl.href = 'bar'; + assert_equals( testEl.itemValue, location.href.replace(/\/[^\/]*$/,'/bar'), 'resolving URLs' ); +}, 'itemValue must reflect the src attribute on area elements'); +test(function () { + var testEl = makeEl('link',{itemprop:'foo',href:'http://example.org/'}); + assert_equals( testEl.itemValue, 'http://example.org/', 'reading' ); + testEl.href = 'http://example.net/'; + assert_equals( testEl.itemValue, 'http://example.net/', 'reading after change' ); + testEl.itemValue = 'http://example.com/'; + assert_equals( testEl.href, 'http://example.com/', 'writing (checking href)' ); + assert_equals( testEl.textContent, '', 'writing (checking textContent)' ); + testEl.href = 'bar'; + assert_equals( testEl.itemValue, location.href.replace(/\/[^\/]*$/,'/bar'), 'resolving URLs' ); +}, 'itemValue must reflect the src attribute on link elements'); +test(function () { + var testEl = makeEl('object',{itemprop:'foo',data:'http://example.org/'},'contained text'); + assert_equals( testEl.itemValue, 'http://example.org/', 'reading' ); + testEl.data = 'http://example.net/'; + assert_equals( testEl.itemValue, 'http://example.net/', 'reading after change' ); + testEl.itemValue = 'http://example.com/'; + assert_equals( testEl.data, 'http://example.com/', 'writing (checking data)' ); + assert_equals( testEl.textContent, 'contained text', 'writing (checking textContent)' ); + assert_equals( makeEl('object',{itemprop:'foo'},'contained text').itemValue, '', 'reading with missing attribute' ); + testEl.data = 'bar'; + assert_equals( testEl.itemValue, location.href.replace(/\/[^\/]*$/,'/bar'), 'resolving URLs' ); +}, 'itemValue must reflect the src attribute on object elements'); +test(function () { + var testEl = makeEl('time',{itemprop:'foo'},'te <span itemprop="bar" itemscope>st</span> ing'); + assert_equals( testEl.itemValue, 'te st ing', 'reading' ); + testEl.innerHTML = 'retest'; + assert_equals( testEl.itemValue, 'retest', 'reading after change' ); + testEl.itemValue = '2001-02-03T04:05:06Z'; + assert_equals( testEl.dateTime, '', 'writing (checking dateTime)' ); + assert_equals( testEl.textContent, '2001-02-03T04:05:06Z', 'writing (checking textContent)' ); +}, 'itemValue must reflect the textContent of time elements with no datetime attribute'); +test(function () { + var testEl = makeEl('time',{itemprop:'foo',datetime:'test'},'te <span itemprop="bar" itemscope>st</span> ing'); + assert_equals( testEl.itemValue, 'test', 'reading' ); + testEl.dateTime = 'retest'; + assert_equals( testEl.itemValue, 'retest', 'reading after change' ); + testEl.itemValue = '2001-02-03T04:05:06Z'; + assert_equals( testEl.dateTime, '2001-02-03T04:05:06Z', 'writing (checking dateTime)' ); + assert_equals( testEl.textContent, 'te st ing', 'writing (checking textContent)' ); +}, 'itemValue must reflect the datetime attribute of time elements with a datetime attribute'); +test(function () { + var testEl = makeEl('div',{itemprop:'foo'},'te <span itemprop="bar" itemscope>st</span> ing'); + assert_equals( testEl.itemValue, 'te st ing', 'reading' ); + testEl.innerHTML = 're<strong>te</strong>st'; + assert_equals( testEl.itemValue, 'retest', 'reading after change' ); + testEl.itemValue = 'test'; + assert_equals( testEl.textContent, 'test', 'writing' ); +}, 'itemValue must reflect the textContent of other elements'); +test(function () { + var testEl = makeEl('madeuponthespot',{itemprop:'foo'},'te <span itemprop="bar" itemscope>st</span> ing'); + assert_equals( testEl.itemValue, 'te st ing', 'reading' ); + testEl.innerHTML = 're<strong>te</strong>st'; + assert_equals( testEl.itemValue, 'retest', 'reading after change' ); + testEl.itemValue = 'test'; + assert_equals( testEl.textContent, 'test', 'writing' ); +}, 'itemValue must reflect the textContent of unknown elements'); +test(function () { + var testEl = makeEl('madeuponthespot',{itemprop:'foo',content:'test',src:'test',href:'test',data:'test',datetime:'test',value:'test'},'te <span itemprop="bar" itemscope>st</span> ing'); + assert_equals( testEl.itemValue, 'te st ing', 'reading' ); + testEl.innerHTML = 're<strong>te</strong>st'; + assert_equals( testEl.itemValue, 'retest', 'reading after change' ); + testEl.itemValue = 'test'; + assert_equals( testEl.textContent, 'test', 'writing' ); +}, 'itemValue must reflect the textContent of unknown elements with known attributes'); +test(function () { + var testEl = makeEl('input',{itemprop:'foo',value:'test'}); + assert_equals( testEl.itemValue, '', 'reading' ); + testEl.value = 'retest'; + assert_equals( testEl.itemValue, '', 'reading after change' ); +}, 'itemValue must not reflect the value of input elements'); +test(function () { + var testEl, eltypes = [ + makeEl('meta',{itemprop:'foo',content:'test'}), + makeEl('audio',{itemprop:'foo',src:'test'},'fail'), + makeEl('embed',{itemprop:'foo',src:'test'}), + makeEl('iframe',{itemprop:'foo',src:'test'},'fail'), + makeEl('img',{itemprop:'foo',src:'test'}), + makeEl('source',{itemprop:'foo',src:'test'}), + makeEl('track',{itemprop:'foo',src:'test'}), + makeEl('video',{itemprop:'foo',src:'test'},'fail'), + makeEl('a',{itemprop:'foo',href:'test'},'fail'), + makeEl('area',{itemprop:'foo',href:'test'}), + makeEl('link',{itemprop:'foo',href:'test'}), + makeEl('object',{itemprop:'foo',data:'test'},'fail'), + makeEl('time',{itemprop:'foo'},'fail'), + makeEl('time',{itemprop:'foo',datetime:'test'},'fail'), + makeEl('div',{itemprop:'foo'},'test'), + makeEl('madeuponthespot',{itemprop:'foo'},'test'), + makeEl('madeuponthespot',{itemprop:'foo',content:'test',src:'test',href:'test',data:'test',datetime:'test',value:'test'},'test'), + makeEl('input',{itemprop:'foo',value:'test'},'test') + ], beforeValues, i; + for( i = 0; i < eltypes.length; i++ ) { + testEl = eltypes[i]; + beforeValues = testEl.itemValue; + testEl.itemScope = true; + assert_equals( testEl.itemValue, testEl, 'itemscope enabled on '+testEl.tagName+' index '+i ); + testEl.itemScope = false; + assert_equals( testEl.itemValue, beforeValues, 'itemscope disabled on '+testEl.tagName+' index '+i ); + testEl.itemScope = true; + testEl.removeAttribute('itemscope'); + assert_equals( testEl.itemValue, beforeValues, 'itemscope attribute removed on '+testEl.tagName+' index '+i ); + } +}, 'dynamic changes of itemscope should change the value exposed through itemValue'); + +test(function () { + var testEl, eltypes = [ + makeEl('meta',{itemprop:'foo',content:'test'}), + makeEl('audio',{itemprop:'foo',src:'test'},'fail'), + makeEl('embed',{itemprop:'foo',src:'test'}), + makeEl('iframe',{itemprop:'foo',src:'test'},'fail'), + makeEl('img',{itemprop:'foo',src:'test'}), + makeEl('source',{itemprop:'foo',src:'test'}), + makeEl('track',{itemprop:'foo',src:'test'}), + makeEl('video',{itemprop:'foo',src:'test'},'fail'), + makeEl('a',{itemprop:'foo',href:'test'},'fail'), + makeEl('area',{itemprop:'foo',href:'test'}), + makeEl('link',{itemprop:'foo',href:'test'}), + makeEl('object',{itemprop:'foo',data:'test'},'fail'), + makeEl('time',{itemprop:'foo'},'fail'), + makeEl('time',{itemprop:'foo',datetime:'test'},'fail'), + makeEl('div',{itemprop:'foo'},'test'), + makeEl('madeuponthespot',{itemprop:'foo'},'test'), + makeEl('madeuponthespot',{itemprop:'foo',content:'test',src:'test',href:'test',data:'test',datetime:'test',value:'test'},'test'), + makeEl('input',{itemprop:'foo',value:'test'},'test') + ], beforeValues, i; + for( i = 0; i < eltypes.length; i++ ) { + testEl = eltypes[i]; + beforeValues = testEl.itemValue; + testEl.itemProp.remove('foo'); + assert_equals( testEl.itemValue, beforeValues, 'itemprop tokens removed on '+testEl.tagName+' index '+i ); + testEl.removeAttribute('itemprop'); + assert_equals( testEl.itemValue, null, 'itemprop attribute removed on '+testEl.tagName+' index '+i ); + testEl.itemProp.toggle('foo'); + assert_equals( testEl.itemValue, beforeValues, 'itemprop tokens added on '+testEl.tagName+' index '+i ); + } +}, 'dynamic changes of itemprop should change the value exposed through itemValue'); + +/* properties */ +test(function () { + assert_equals( typeof makeEl('div',{}).properties, 'object' ); +}, 'the properties property must be an object'); +test(function () { + var testEl = makeEl('div',{}); + assert_true( testEl.properties instanceof HTMLPropertiesCollection, 'instanceof HTMLPropertiesCollection' ); + assert_true( testEl.properties instanceof HTMLCollection, 'instanceof HTMLCollection' ); + HTMLPropertiesCollection.prototype.customProperty = true; + HTMLCollection.prototype.anotherCustomProperty = true; + assert_true( testEl.properties.customProperty, 'inheritance from HTMLPropertiesCollection' ); + assert_true( testEl.properties.anotherCustomProperty, 'inheritance from HTMLCollection' ); + HTMLPropertiesCollection.prototype.anotherCustomProperty = false; + assert_false( testEl.properties.anotherCustomProperty, 'shadowing by HTMLPropertiesCollection' ); +}, 'the properties property must implement HTMLPropertiesCollection and HTMLCollection'); +test(function () { + var failed = false, elem = makeEl('div',{itemscope:'itemscope'}), realList = elem.properties; + try { + elem.properties = ''; + } catch(e) { + failed = e; + } + assert_equals(elem.properties,realList); + assert_false(failed,'an error was thrown'); +}, 'the properties property must be read-only'); +test(function () { + var testEl = makeEl('div',{}); + assert_equals( testEl.properties, testEl.properties ); +}, 'the properties property must always reference the same object'); +test(function () { + var testEl = makeEl('div',{},'<div itemprop="foo">bar</div>'); + assert_equals( testEl.properties.length, 0, 'length' ); + assert_true( !testEl.properties.item(0), 'item(0)' ); + assert_true( !testEl.properties[0], '[0]' ); + assert_equals( testEl.properties.namedItem('foo').length, 0, 'namedItem' ); + assert_true( !testEl.properties['foo'], '[namedItem]' ); + assert_equals( testEl.properties.namedItem('foo').getValues().length, 0, 'namedItem' ); + assert_equals( testEl.properties.names.length, 0, 'names' ); +}, 'the properties collection must be empty if the element does not have an itemscope property'); +test(function() { + var testEl = makeEl('div',{},'<div itemprop="foo">bar</div>'); + assert_throws( new TypeError(), function() { testEl.properties('foo'); } ); + assert_throws( new TypeError(), function() { testEl.properties(0); } ); +}, 'the properties collection must not support legacycaller'); +test(function () { + var testEl = makeEl('div',{},'<div itemprop="foo">bar</div>'); + testEl.itemScope = true; + assert_equals( testEl.properties.length, 1, 'length' ); + assert_equals( testEl.properties.item(0), testEl.firstChild, 'item(0)' ); + assert_equals( testEl.properties[0], testEl.firstChild, '[0]' ); + assert_equals( testEl.properties.namedItem('foo').length, 1, 'namedItem' ); + assert_equals( testEl.properties['foo'].length, 1, '[namedItem]' ); + assert_equals( testEl.properties.namedItem('foo').getValues().length, 1, 'namedItem' ); + assert_equals( testEl.properties.names.length, 1, 'names' ); +}, 'the properties collection must become populated if the element is given an itemscope property'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo">bar</div>'); + testEl.itemScope = false; + assert_equals( testEl.properties.length, 0, 'length' ); + assert_true( !testEl.properties.item(0), 'item(0)' ); + assert_true( !testEl.properties[0], '[0]' ); + assert_equals( testEl.properties.namedItem('foo').length, 0, 'namedItem' ); + assert_true( !testEl.properties['foo'], '[namedItem]' ); + assert_equals( testEl.properties.namedItem('foo').getValues().length, 0, 'namedItem' ); + assert_equals( testEl.properties.names.length, 0, 'names' ); +}, 'the properties collection must become empty if the element\'s itemscope property is removed'); +//properties.item and properties.length (part 1) +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemprop="bar"><div itemprop="foo"></div><div itemprop="foo"></div></div><div itemprop="baz qux"></div>'); + assert_equals( testEl.properties.length, 5 ); +}, 'properties.length must be the total number of properties'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemprop="bar"><div itemprop="foo"></div></div><div itemprop="baz qux"></div>'); + assert_equals( testEl.properties.item(0), testEl.childNodes[0], 'item(0)' ); + assert_equals( testEl.properties.item(1), testEl.childNodes[1], 'item(1)' ); + assert_equals( testEl.properties.item(2), testEl.childNodes[1].childNodes[0], 'item(2)' ); + assert_equals( testEl.properties.item(3), testEl.childNodes[2], 'item(3)' ); +}, 'properties.item must give each property in tree order'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemprop="bar"><div itemprop="foo"></div></div><div itemprop="baz qux"></div>'); + assert_equals( testEl.properties.item(4), null, 'positive index' ); + assert_equals( testEl.properties.item(-1), null, 'negative index' ); +}, 'properties.item must give null for out of range index'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemprop="bar"><div itemprop="foo"></div></div><div itemprop="baz qux"></div>'); + assert_equals( testEl.properties[0], testEl.childNodes[0], '[0]' ); + assert_equals( testEl.properties[1], testEl.childNodes[1], '[1]' ); + assert_equals( testEl.properties[2], testEl.childNodes[1].childNodes[0], '[2]' ); + assert_equals( testEl.properties[3], testEl.childNodes[2], '[3]' ); +}, 'properties[index] must give each property in tree order'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemprop="bar"><div itemprop="foo"></div></div><div itemprop="baz qux"></div>'); + assert_equals( testEl.properties[4], window.undefined, 'positive index' ); + assert_equals( testEl.properties[-1], window.undefined, 'negative index' ); +}, 'properties[index] must give undefined for out of range index'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemscope itemprop="foo"><div itemprop="bar"></div></div><div><div itemprop="baz"></div></div>'); + assert_equals( testEl.properties.length, 2, 'length' ); + assert_equals( testEl.properties.item(0), testEl.firstChild, 'properties.item(0)' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'properties[0]' ); + assert_equals( testEl.properties.item(1), testEl.childNodes[1].firstChild, 'properties.item(1)' ); + assert_equals( testEl.properties[1], testEl.childNodes[1].firstChild, 'properties[1]' ); +}, 'properties.item and length must ignore properties of nested items'); +test(function () { + //note, itemref ordering is reversed compared with the next test to catch failed sorting algorithms + var parEl = makeEl('div',{},'<div itemprop="foo" id="id1"></div><div itemscope itemref="id2 id1"><div itemprop="bar"></div></div><div itemprop="baz" id="id2"><div itemprop="qux"></div></div>'); + var testEl = parEl.childNodes[1]; + document.body.appendChild(parEl); + var propLength = testEl.properties.length; + var item0 = testEl.properties.item(0); + var square0 = testEl.properties[0]; + var item1 = testEl.properties.item(1); + var square1 = testEl.properties[1]; + var item2 = testEl.properties.item(2); + var square2 = testEl.properties[2]; + var item3 = testEl.properties.item(3); + var square3 = testEl.properties[3]; + document.body.removeChild(parEl); + assert_equals( propLength, 4, 'length' ); + assert_equals( item0, parEl.firstChild, 'properties.item(0)' ); + assert_equals( square0, parEl.firstChild, 'properties[0]' ); + assert_equals( item1, testEl.firstChild, 'properties.item(1)' ); + assert_equals( square1, testEl.firstChild, 'properties[1]' ); + assert_equals( item2, parEl.childNodes[2], 'properties.item(2)' ); + assert_equals( square2, parEl.childNodes[2], 'properties[2]' ); + assert_equals( item3, parEl.childNodes[2].firstChild, 'properties.item(3)' ); + assert_equals( square3, parEl.childNodes[2].firstChild, 'properties[3]' ); +}, 'properties.item and length must see items added with itemref when attached to the document\'s DOM'); +test(function () { + var parEl = makeEl('div',{},'<div itemprop="foo" id="id1"></div><div itemscope itemref="id1 id2"><div itemprop="bar"></div></div><div itemprop="baz" id="id2"><div itemprop="qux"></div></div>'); + var testEl = parEl.childNodes[1]; + assert_equals( testEl.properties.length, 4, 'length' ); + assert_equals( testEl.properties.item(0), parEl.firstChild, 'properties.item(0)' ); + assert_equals( testEl.properties[0], parEl.firstChild, 'properties[0]' ); + assert_equals( testEl.properties.item(1), testEl.firstChild, 'properties.item(1)' ); + assert_equals( testEl.properties[1], testEl.firstChild, 'properties[1]' ); + assert_equals( testEl.properties.item(2), parEl.childNodes[2], 'properties.item(2)' ); + assert_equals( testEl.properties[2], parEl.childNodes[2], 'properties[2]' ); + assert_equals( testEl.properties.item(3), parEl.childNodes[2].firstChild, 'properties.item(3)' ); + assert_equals( testEl.properties[3], parEl.childNodes[2].firstChild, 'properties[3]' ); +}, 'properties.item and length must see items added with itemref'); +test(function () { + var parEl = makeEl('div',{},'<div itemscope itemref="id1"></div><div itemprop="foo" id="id1"><div itemprop="bar"></div></div><div itemprop="baz" id="id1"></div>'); + var testEl = parEl.childNodes[0]; + assert_equals( testEl.properties.length, 2, 'length' ); + assert_equals( testEl.properties.item(0), parEl.childNodes[1], 'properties.item(0)' ); + assert_equals( testEl.properties.item(1), parEl.childNodes[1].firstChild, 'properties.item(1)' ); + document.body.appendChild(parEl) + var length = testEl.properties.length; + var item0 = testEl.properties.item(0); + var item1 = testEl.properties.item(0); + document.body.removeChild(parEl) + assert_equals( testEl.properties.length, 2, 'length (attached to document)' ); + assert_equals( testEl.properties.item(0), parEl.childNodes[1], 'properties.item(0) (attached to document)' ); + assert_equals( testEl.properties.item(1), parEl.childNodes[1].firstChild, 'properties.item(1) (attached to document)' ); +}, 'itemref must reference the first element with a given ID'); +test(function () { + var parEl = makeEl('div',{},'<div itemscope itemref="id1 id1"></div><div itemprop="foo" id="id1"><div itemprop="bar"></div></div><div itemprop="baz" id="id1"></div>'); + var testEl = parEl.childNodes[0]; + assert_equals( testEl.properties.length, 2, 'length' ); + assert_equals( testEl.properties.item(0), parEl.childNodes[1], 'properties.item(0)' ); + assert_equals( testEl.properties.item(1), parEl.childNodes[1].firstChild, 'properties.item(1)' ); + document.body.appendChild(parEl) + var length = testEl.properties.length; + var item0 = testEl.properties.item(0); + var item1 = testEl.properties.item(0); + document.body.removeChild(parEl) + assert_equals( testEl.properties.length, 2, 'length (attached to document)' ); + assert_equals( testEl.properties.item(0), parEl.childNodes[1], 'properties.item(0) (attached to document)' ); + assert_equals( testEl.properties.item(1), parEl.childNodes[1].firstChild, 'properties.item(1) (attached to document)' ); +}, 'itemref must ignore duplicated IDs'); +test(function () { + var parEl = makeEl('div',{},'<div itemscope itemref="id0 id1"></div><div itemprop="foo" id="id1"><div itemprop="bar"></div></div>'); + var testEl = parEl.childNodes[0]; + assert_equals( testEl.properties.length, 2, 'length' ); + assert_equals( testEl.properties.item(0), parEl.childNodes[1], 'properties.item(0)' ); + assert_equals( testEl.properties.item(1), parEl.childNodes[1].firstChild, 'properties.item(1)' ); + document.body.appendChild(parEl) + var length = testEl.properties.length; + var item0 = testEl.properties.item(0); + var item1 = testEl.properties.item(0); + document.body.removeChild(parEl) + assert_equals( testEl.properties.length, 2, 'length (attached to document)' ); + assert_equals( testEl.properties.item(0), parEl.childNodes[1], 'properties.item(0) (attached to document)' ); + assert_equals( testEl.properties.item(1), parEl.childNodes[1].firstChild, 'properties.item(1) (attached to document)' ); +}, 'itemref must ignore non-existent IDs'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope',itemref:'id1'}); + var dummyEl = makeEl('div',{id:'id1',itemprop:'foo'}); + assert_equals( testEl.properties.length, 0 ); +}, 'itemref in a dislocated tree must not reference elements from another dislocated tree'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope',itemref:'id1'}); + var dummyEl = makeEl('div',{id:'id1',itemprop:'foo'}); + document.body.appendChild(dummyEl); + var tmp = testEl.properties.length; + document.body.removeChild(dummyEl); + assert_equals( tmp, 0 ); +}, 'itemref in a dislocated tree must not reference elements from the main document'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope',itemref:'id1'}); + var dummyEl = makeEl('div',{id:'id1',itemprop:'foo'}); + document.body.appendChild(testEl); + var tmp = testEl.properties.length; + document.body.removeChild(testEl); + assert_equals( tmp, 0 ); +}, 'itemref in the main document must not reference elements from a dislocated tree'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div>'); + assert_equals( testEl.properties.length, 1, 'length (before test)' ); + assert_equals( testEl.properties.item(0), testEl.firstChild, 'properties.item(0) (before test)' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'properties[0] (before test)' ); + testEl.appendChild(makeEl('div',{itemprop:'bar'})); + assert_equals( testEl.properties.length, 2, 'length after adding a child' ); + assert_equals( testEl.properties.item(1), testEl.childNodes[1], 'properties.item(1) after adding a child' ); + assert_equals( testEl.properties[1], testEl.childNodes[1], 'properties[1] after adding a child' ); + testEl.lastChild.appendChild(makeEl('div',{itemprop:'foo'})); + assert_equals( testEl.properties.length, 3, 'length after adding a child with duplicated name' ); + assert_equals( testEl.properties.item(2), testEl.childNodes[1].firstChild, 'properties.item(2) after adding a child with duplicated name' ); + assert_equals( testEl.properties[2], testEl.childNodes[1].firstChild, 'properties[2] after adding a child with duplicated name' ); + testEl.lastChild.removeChild(testEl.lastChild.firstChild); + assert_equals( testEl.properties.length, 2, 'length after removing a child' ); + assert_true( !testEl.properties.item(2), 'properties.item(1) after removing a child' ); + assert_true( !testEl.properties[2], 'properties[1] after removing a child' ); +}, 'properties.item and length must update when adding property elements'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemprop="bar"></div>'); + assert_equals( testEl.properties.length, 2, 'length (before test)' ); + assert_equals( testEl.properties.item(0), testEl.firstChild, 'properties.item(0) (before test)' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'properties[0] (before test)' ); + assert_equals( testEl.properties.item(1), testEl.childNodes[1], 'properties.item(1) (before test)' ); + assert_equals( testEl.properties[1], testEl.childNodes[1], 'properties[1] (before test)' ); + testEl.appendChild(testEl.firstChild); + assert_equals( testEl.properties.length, 2, 'length (after test)' ); + assert_equals( testEl.properties.item(0), testEl.firstChild, 'properties.item(0) (after test)' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'properties[0] (after test)' ); + assert_equals( testEl.properties.item(1), testEl.childNodes[1], 'properties.item(1) (after test)' ); + assert_equals( testEl.properties[1], testEl.childNodes[1], 'properties[1] (after test)' ); +}, 'properties.item must update when re-ordering property elements, but length must not'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div></div>'); + assert_equals( testEl.properties.length, 1, 'length (before test)' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'properties[0] (before test)' ); + testEl.lastChild.itemProp.toggle('bar'); + assert_equals( testEl.properties.length, 2, 'length (after test 1)' ); + assert_equals( testEl.properties.item(0), testEl.firstChild, 'properties.item(0) after adding a token' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'properties[0] after adding a token' ); + assert_equals( testEl.properties.item(1), testEl.childNodes[1], 'properties.item(1) after adding a token' ); + assert_equals( testEl.properties[1], testEl.childNodes[1], 'properties[1] after adding a token' ); + testEl.lastChild.removeAttribute('itemprop'); + assert_equals( testEl.properties.length, 1, 'length after removing an attribute' ); + assert_equals( testEl.properties.item(0), testEl.firstChild, 'properties.item(0) after removing an attribute' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'properties[0] after removing an attribute' ); + assert_true( !testEl.properties.item(1), 'properties.item(1) after removing an attribute' ); + assert_true( !testEl.properties[1], 'properties[1] after removing an attribute' ); +}, 'properties.item and length must update when changing itemProp of children'); +test(function () { + var parEl = makeEl('div',{},'<div itemprop="foo"><div itemprop="bar"></div></div><div itemscope itemref="id1"></div>'); + var testEl = parEl.childNodes[1]; + assert_equals( testEl.properties.length, 0, 'length (before test)' ); + parEl.firstChild.id = 'id1'; + assert_equals( testEl.properties.length, 2, 'length after id is created' ); + assert_equals( testEl.properties.item(0), parEl.firstChild, 'properties.item(0) after id is created' ); + assert_equals( testEl.properties[0], parEl.firstChild, 'properties[0] after id is created' ); + assert_equals( testEl.properties.item(1), parEl.firstChild.firstChild, 'properties.item(1) after id is created' ); + assert_equals( testEl.properties[1], parEl.firstChild.firstChild, 'properties[1] after id is created' ); + parEl.firstChild.removeAttribute('id'); + assert_equals( testEl.properties.length, 0, 'length after removing an attribute' ); + assert_true( !testEl.properties.item(0), 'properties.item(0) after removing an attribute' ); + assert_true( !testEl.properties[0], 'properties[0] after removing an attribute' ); + document.body.appendChild(parEl); + var beflength = testEl.properties.length; + parEl.firstChild.id = 'id1'; + var length1 = testEl.properties.length, + item0 = testEl.properties.item(0), + prop0 = testEl.properties[0], + item1 = testEl.properties.item(1), + prop1 = testEl.properties[1]; + parEl.firstChild.removeAttribute('id'); + var length2 = testEl.properties.length, + bitem = !testEl.properties.item(0), + bprop = !testEl.properties[0]; + document.body.removeChild(parEl); + assert_equals( beflength, 0, 'length (before test) when appended to document' ); + assert_equals( length1, 2, 'length after id is created when appended to document' ); + assert_equals( item0, parEl.firstChild, 'properties.item(0) after id is created when appended to document' ); + assert_equals( prop0, parEl.firstChild, 'properties[0] after id is created when appended to document' ); + assert_equals( item1, parEl.firstChild.firstChild, 'properties.item(1) after id is created when appended to document' ); + assert_equals( prop1, parEl.firstChild.firstChild, 'properties[1] after id is created when appended to document' ); + assert_equals( length2, 0, 'length after removing an attribute when appended to document' ); + assert_true( bitem, 'properties.item(0) after removing an attribute when appended to document' ); + assert_true( bprop, 'properties[0] after removing an attribute when appended to document' ); +}, 'properties.item and length must update when changing id of referenced sibling'); +test(function () { + var parEl = makeEl('div',{},'<div itemprop="foo"><div itemprop="bar"></div></div><div itemscope itemref="id1"></div><div itemprop="baz" id="id1"></div>'); + var testEl = parEl.childNodes[1]; + assert_equals( testEl.properties.length, 1, 'length (before test)' ); + assert_equals( testEl.properties.item(0), parEl.lastChild, 'properties.item(0) (before test)' ); + assert_equals( testEl.properties[0], parEl.lastChild, 'properties[0] (before test)' ); + parEl.firstChild.id = 'id1'; + assert_equals( testEl.properties.length, 2, 'length after id is created' ); + assert_equals( testEl.properties.item(0), parEl.firstChild, 'properties.item(0) after id is created' ); + assert_equals( testEl.properties[0], parEl.firstChild, 'properties[0] after id is created' ); + assert_equals( testEl.properties.item(1), parEl.firstChild.firstChild, 'properties.item(1) after id is created' ); + assert_equals( testEl.properties[1], parEl.firstChild.firstChild, 'properties[1] after id is created' ); + parEl.firstChild.removeAttribute('id'); + assert_equals( testEl.properties.length, 1, 'length after removing an attribute' ); + assert_equals( testEl.properties.item(0), parEl.lastChild, 'properties.item(0) after removing an attribute' ); + assert_equals( testEl.properties[0], parEl.lastChild, 'properties[0] after removing an attribute' ); + document.body.appendChild(parEl); + var beflength = testEl.properties.length, + befitem = testEl.properties.item(0), + befprop = testEl.properties[0]; + parEl.firstChild.id = 'id1'; + var length1 = testEl.properties.length, + item0 = testEl.properties.item(0), + prop0 = testEl.properties[0], + item1 = testEl.properties.item(1), + prop1 = testEl.properties[1]; + parEl.firstChild.removeAttribute('id'); + var length2 = testEl.properties.length, + afitem = testEl.properties.item(0), + afprop = testEl.properties[0]; + document.body.removeChild(parEl); + assert_equals( beflength, 1, 'length (before test) when appended to document' ); + assert_equals( befitem, parEl.lastChild, 'properties.item(0) (before test)' ); + assert_equals( befprop, parEl.lastChild, 'properties[0] (before test)' ); + assert_equals( length1, 2, 'length after id is created when appended to document' ); + assert_equals( item0, parEl.firstChild, 'properties.item(0) after id is created when appended to document' ); + assert_equals( prop0, parEl.firstChild, 'properties[0] after id is created when appended to document' ); + assert_equals( item1, parEl.firstChild.firstChild, 'properties.item(1) after id is created when appended to document' ); + assert_equals( prop1, parEl.firstChild.firstChild, 'properties[1] after id is created when appended to document' ); + assert_equals( length2, 1, 'length after removing an attribute when appended to document' ); + assert_equals( afitem, parEl.lastChild, 'properties.item(0) after removing an attribute when appended to document' ); + assert_equals( afprop, parEl.lastChild, 'properties[0] after removing an attribute when appended to document' ); +}, 'properties.item and length must update when changing duplicated id of referenced sibling'); +test(function () { + var parEl = makeEl('div',{},'<div id="id1" itemprop="foo"></div><div itemscope></div>'); + var testEl = parEl.childNodes[1]; + assert_equals( testEl.properties.length, 0, 'length (before test)' ); + testEl.itemRef.toggle('id1'); + assert_equals( testEl.properties.length, 1, 'length after itemref is changed' ); + assert_equals( testEl.properties.item(0), parEl.firstChild, 'properties.item(0) after itemref is changed' ); + assert_equals( testEl.properties[0], parEl.firstChild, 'properties[0] after itemref is changed' ); + testEl.removeAttribute('itemref'); + assert_equals( testEl.properties.length, 0, 'length after itemref is removed' ); + assert_true( !testEl.properties.item(0), 'properties.item(0) itemref is removed' ); + assert_true( !testEl.properties[0], 'properties[0] itemref is removed' ); + document.body.appendChild(parEl); + var beflength = testEl.properties.length; + testEl.itemRef.toggle('id1'); + var length1 = testEl.properties.length, + item0 = testEl.properties.item(0), + prop0 = testEl.properties[0]; + testEl.removeAttribute('itemref'); + var length2 = testEl.properties.length, + bitem = !testEl.properties.item(0), + bprop = !testEl.properties[0]; + document.body.removeChild(parEl); + assert_equals( beflength, 0, 'length (before test) when appended to document' ); + assert_equals( length1, 1, 'length after itemref is changed when appended to document' ); + assert_equals( item0, parEl.firstChild, 'properties.item(0) after itemref is changed when appended to document' ); + assert_equals( prop0, parEl.firstChild, 'properties[0] after itemref is changed when appended to document' ); + assert_equals( length2, 0, 'length after itemref is removed when appended to document' ); + assert_true( bitem, 'properties.item(0) after itemref is removed when appended to document' ); + assert_true( bprop, 'properties[0] after itemref is removed when appended to document' ); +}, 'properties.item and length must update when changing itemref to point to an element'); +test(function () { + var parEl = makeEl('div',{},'<div id="id1"><div></div></div><div itemscope itemref="id1"></div>'); + var testEl = parEl.childNodes[1]; + assert_equals( testEl.properties.length, 0, 'length (before test)' ); + parEl.firstChild.appendChild(makeEl('div',{itemprop:'foo'})); + assert_equals( testEl.properties.length, 1, 'length after a referenced element is added' ); + assert_equals( testEl.properties.item(0), parEl.firstChild.lastChild, 'properties.item(0) after a referenced element is added' ); + assert_equals( testEl.properties[0], parEl.firstChild.lastChild, 'properties[0] after a referenced element is added' ); + parEl.firstChild.firstChild.itemProp.toggle('bar'); + assert_equals( testEl.properties.length, 2, 'length after a referenced itemprop is changed' ); + assert_equals( testEl.properties.item(0), parEl.firstChild.firstChild, 'properties.item(0) after a referenced itemprop is changed' ); + assert_equals( testEl.properties[0], parEl.firstChild.firstChild, 'properties[0] after a referenced itemprop is changed' ); + assert_equals( testEl.properties.item(1), parEl.firstChild.lastChild, 'properties.item(1) after a referenced itemprop is changed' ); + assert_equals( testEl.properties[1], parEl.firstChild.lastChild, 'properties[1] after a referenced itemprop is changed' ); + parEl.firstChild.removeChild(parEl.firstChild.firstChild); + assert_equals( testEl.properties.length, 1, 'length after a referenced element is removed' ); + assert_equals( testEl.properties.item(0), parEl.firstChild.firstChild, 'properties.item(0) after a referenced element is removed' ); + assert_equals( testEl.properties[0], parEl.firstChild.firstChild, 'properties[0] after a referenced element is removed' ); + assert_true( !testEl.properties.item(1), 'properties.item(1) after a referenced element is removed' ); + assert_true( !testEl.properties[1], 'properties[1] after a referenced element is removed' ); + parEl.innerHTML = '<div id="id1"><div></div></div><div itemscope itemref="id1"></div>'; + testEl = parEl.childNodes[1]; + document.body.appendChild(parEl); + var beflength = testEl.properties.length; + parEl.firstChild.appendChild(makeEl('div',{itemprop:'foo'})); + var length1 = testEl.properties.length, + item0a = testEl.properties.item(0), + prop0a = testEl.properties[0], + targ0a = parEl.firstChild.lastChild; + parEl.firstChild.firstChild.itemProp.toggle('bar'); + var length2 = testEl.properties.length, + item0b = testEl.properties.item(0), + prop0b = testEl.properties[0]; + item1b = testEl.properties.item(1), + prop1b = testEl.properties[1], + targ0b = parEl.firstChild.firstChild, + targ1b = parEl.firstChild.lastChild; + parEl.firstChild.removeChild(parEl.firstChild.firstChild); + var length3 = testEl.properties.length, + item0c = testEl.properties.item(0), + prop0c = testEl.properties[0]; + item1c = testEl.properties.item(1), + prop1c = testEl.properties[1], + targ0c = parEl.firstChild.firstChild; + document.body.removeChild(parEl); + assert_equals( beflength, 0, 'length (before test) when appended to document' ); + assert_equals( length1, 1, 'length after a referenced element is added when appended to document' ); + assert_equals( item0a, targ0a, 'properties.item(0) after a referenced element is added when appended to document' ); + assert_equals( prop0a, targ0a, 'properties[0] after a referenced element is added when appended to document' ); + assert_equals( length2, 2, 'length after a referenced itemprop is changed when appended to document' ); + assert_equals( item0b, targ0b, 'properties.item(0) after a referenced itemprop is changed when appended to document' ); + assert_equals( prop0b, targ0b, 'properties[0] after a referenced itemprop is changed when appended to document' ); + assert_equals( item1b, targ1b, 'properties.item(1) after a referenced itemprop is changed when appended to document' ); + assert_equals( prop1b, targ1b, 'properties[1] after a referenced itemprop is changed when appended to document' ); + assert_equals( length3, 1, 'length after a referenced element is removed when appended to document' ); + assert_equals( item0c, targ0c, 'properties.item(0) after a referenced element is removed when appended to document' ); + assert_equals( prop0c, targ0c, 'properties[0] after a referenced element is removed when appended to document' ); + assert_true( !item1c, 'properties.item(1) after a referenced element is removed when appended to document' ); + assert_true( !prop1c, 'properties[1] after a referenced element is removed when appended to document' ); +}, 'properties.item and length must update when changing children of elements referenced through itemref'); +test(function () { + var parEl = makeEl('div',{},'<div id="id1" itemprop="foo"></div><div itemscope itemref="id1"><div itemprop="foo"></div></div>'); + var testEl = parEl.childNodes[1]; + assert_equals( testEl.properties.length, 2, 'length (before test)' ); + assert_equals( testEl.properties[0], parEl.firstChild, 'properties[0] (before test)' ); + assert_equals( testEl.properties[1], testEl.firstChild, 'properties[1] (before test)' ); + document.body.appendChild(testEl); + var step1length = testEl.properties.length; + var step1prop0 = testEl.properties[0]; + var step1prop1 = testEl.properties[1]; + parEl.appendChild(testEl); + assert_equals( step1length, 1, 'length after changing parent' ); + assert_equals( step1prop0, testEl.firstChild, 'properties[0] after changing parent' ); + assert_true( !step1prop1, 'properties[1] after changing parent' ); + assert_equals( testEl.properties.length, 2, 'length after re-parenting' ); + assert_equals( testEl.properties[0], parEl.firstChild, 'properties[0] after re-parenting' ); + assert_equals( testEl.properties[1], testEl.firstChild, 'properties[1] after re-parenting' ); + document.body.appendChild(parEl); + var step2length = testEl.properties.length; + var step2prop0 = testEl.properties[0]; + var step2prop1 = testEl.properties[1]; + document.createElement('div').appendChild(testEl); + var step3length = testEl.properties.length; + var step3prop0 = testEl.properties[0]; + var step3prop1 = testEl.properties[1]; + parEl.appendChild(testEl); + var step4length = testEl.properties.length; + var step4prop0 = testEl.properties[0]; + var step4prop1 = testEl.properties[1]; + document.body.removeChild(parEl); + assert_equals( step2length, 2, 'length (before test) when appended to document' ); + assert_equals( step2prop0, parEl.firstChild, 'properties[0] (before test) when appended to document' ); + assert_equals( step2prop1, testEl.firstChild, 'properties[1] (before test) when appended to document' ); + assert_equals( step3length, 1, 'length after changing parent when appended to document' ); + assert_equals( step3prop0, testEl.firstChild, 'properties[0] after changing parent when appended to document' ); + assert_true( !step3prop1, 'properties[1] after changing parent when appended to document' ); + assert_equals( step4length, 2, 'length after re-parenting when appended to document' ); + assert_equals( step4prop0, parEl.firstChild, 'properties[0] after re-parenting when appended to document' ); + assert_equals( step4prop1, testEl.firstChild, 'properties[1] after re-parenting when appended to document' ); +}, 'properties.item and length must update when appending elements with itemref to different parents'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div><div itemprop="foo"></div></div>'); + assert_equals( testEl.properties.length, 1, 'length (before test)' ); + assert_equals( testEl.properties.item(0), testEl.firstChild.firstChild, 'properties.item(0) (before test)' ); + assert_equals( testEl.properties[0], testEl.firstChild.firstChild, 'properties[0] (before test)' ); + testEl.firstChild.itemScope = true; + assert_equals( testEl.properties.length, 0, 'length after setting itemscope' ); + assert_true( !testEl.properties.item(0), 'properties.item(0) after setting itemscope' ); + assert_true( !testEl.properties[0], 'properties[0] after setting itemscope' ); + testEl.firstChild.removeAttribute('itemscope'); + assert_equals( testEl.properties.length, 1, 'length after removing itemscope attribute' ); + assert_equals( testEl.properties.item(0), testEl.firstChild.firstChild, 'properties.item(0) after removing itemscope attribute' ); + assert_equals( testEl.properties[0], testEl.firstChild.firstChild, 'properties[0] after removing itemscope attribute' ); +}, 'properties.item and length must update when changing itemscope of children'); +//properties.namedItem +test(function () { + assert_equals( typeof makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div>').properties.namedItem('foo'), 'object' ); +}, 'the namedItem must return an object'); +test(function () { + assert_equals( typeof makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div>').properties['foo'], 'object' ); +}, '.properties[] must also act as .properties.namedItem() when there are matching properties'); +test(function () { + assert_equals( typeof makeEl('div',{itemscope:'itemscope'},'').properties.namedItem('foo'), 'object' ); +}, 'the namedItem must return an object even if there are no matching properties'); +test(function () { + assert_equals( typeof makeEl('div',{itemscope:'itemscope'},'').properties['foo'], 'undefined' ); +}, '.properties[] must return undefined when no property exists with the given name'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div>'); + var PNL = testEl.properties.namedItem('foo'); + assert_equals( PNL, testEl.properties.namedItem('foo'), 'before modification' ); + testEl.innerHTML = ''; + assert_equals( PNL, testEl.properties.namedItem('foo'), 'after modification' ); +}, 'namedItem must return the same object for the same property name'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div>'); + assert_false( testEl.properties.namedItem('foo') == testEl.properties.namedItem('bar') ); +}, 'namedItem must return a different object for a different property name'); +test(function () { + assert_false( makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div>').properties.namedItem('foo') == makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div>').properties.namedItem('foo') ); +}, 'namedItem must return a different object for different elements with the same property name'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div>'); + assert_equals( testEl.properties.namedItem('foo'), testEl.properties['foo'] ); +}, 'namedItem() and properties[] must return the same object for the same property name'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div>'); + assert_true( testEl.properties.namedItem('foo') instanceof PropertyNodeList, 'instanceof PropertyNodeList' ); + assert_true( testEl.properties.namedItem('foo') instanceof NodeList, 'instanceof NodeList' ); + PropertyNodeList.prototype.customProperty = true; + NodeList.prototype.anotherCustomProperty = true; + assert_true( testEl.properties.namedItem('foo').customProperty, 'inheritance from PropertyNodeList' ); + assert_true( testEl.properties.namedItem('foo').anotherCustomProperty, 'inheritance from NodeList' ); + PropertyNodeList.prototype.anotherCustomProperty = false; + assert_false( testEl.properties.anotherCustomProperty, 'shadowing by PropertyNodeList' ); +}, 'the properties property must implement PropertyNodeList and NodeList'); +test(function () { + var failed = false, elem = makeEl('div',{itemscope:'itemscope'}); + try { + elem.properties.namedItem = 'pass'; + } catch(e) { + failed = e; + } + assert_equals(elem.properties.namedItem,'pass'); + assert_false(failed,'an error was thrown'); +}, 'the namedItem property must be read/write'); +test(function () { + //also tests for sort ordering, which is fairly simple in this case + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemprop="bar"><div itemprop="baz qux"></div></div><div itemprop="foo"></div>'); + assert_equals( testEl.properties.namedItem('foo').length, 2, 'length of foo' ); + assert_equals( testEl.properties.namedItem('bar').length, 1, 'length of bar' ); + assert_equals( testEl.properties.namedItem('baz').length, 1, 'length of baz' ); + assert_equals( testEl.properties.namedItem('qux').length, 1, 'length of qux' ); + assert_equals( testEl.properties.namedItem('foo')[0], testEl.firstChild, 'first foo' ); + assert_equals( testEl.properties.namedItem('foo')[1], testEl.lastChild, 'last foo' ); + assert_equals( testEl.properties.namedItem('bar')[0], testEl.childNodes[1], 'bar' ); + assert_equals( testEl.properties.namedItem('baz')[0], testEl.childNodes[1].firstChild, 'baz' ); + assert_equals( testEl.properties.namedItem('qux')[0], testEl.childNodes[1].firstChild, 'qux' ); +}, 'PropertyNodeList must contain the correct properties'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemprop="bar"><div itemprop="FOO"></div><div itemprop="foo FOO foo"></div></div><div itemprop="baz qux"></div>'); + assert_equals( testEl.properties.namedItem('foo').length, 2, 'length of foo' ); + assert_equals( testEl.properties.namedItem('FOO').length, 2, 'length of FOO' ); + assert_equals( testEl.properties.namedItem('foo')[0], testEl.firstChild, 'first foo' ); + assert_equals( testEl.properties.namedItem('foo')[1], testEl.childNodes[1].lastChild, 'last foo' ); + assert_equals( testEl.properties.namedItem('FOO')[0], testEl.childNodes[1].firstChild, 'first FOO' ); + assert_equals( testEl.properties.namedItem('FOO')[1], testEl.childNodes[1].lastChild, 'last FOO' ); +}, 'PropertyNodeList must be case sensitive'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo bar"></div>'); + assert_equals( testEl.properties.namedItem('foo bar').length, 0, 'space' ); + testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo\tbar"></div>'); + assert_equals( testEl.properties.namedItem('foo\tbar').length, 0, 'tab' ); + testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo\rbar"></div>'); + assert_equals( testEl.properties.namedItem('foo\rbar').length, 0, 'carriage return' ); + testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo\nbar"></div>'); + assert_equals( testEl.properties.namedItem('foo\nbar').length, 0, 'newline' ); + testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo\fbar"></div>'); + assert_equals( testEl.properties.namedItem('foo\fbar').length, 0, 'formfeed' ); +}, 'namedItem must not match property names containing whitespace'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="|§!"#¤%&/()=?`\\@£${[]}´€^¨~\'*,;.:-_<>åøæÅØÆ"></div>'); + assert_equals( testEl.properties.namedItem('|§!"#¤%&/()=?`\\@£${[]}´€^¨~\'*,;.:-_<>åøæÅØÆ').length, 1 ); +}, 'namedItem must match property names containing other special characters'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'}); + var PNL = testEl.properties.namedItem('foo'); + testEl.innerHTML = '<div itemprop="foo"></div>'; + assert_equals( PNL.length, 1 ); + assert_equals( PNL[0], testEl.firstChild ); +}, 'PropertyNodeList must be live'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemscope><div itemprop="foo"></div><div itemprop="bar"></div></div>'); + assert_equals( testEl.properties.namedItem('foo').length, 1, 'length of foo' ); + assert_equals( testEl.properties.namedItem('foo')[0], testEl.firstChild, 'item 0' ); + assert_equals( testEl.properties.namedItem('bar').length, 0, 'length of bar' ); +}, 'PropertyNodeList must ignore properties of nested items'); +test(function () { + + //note, itemref ordering is reversed compared with the next test to catch failed sorting algorithms - not that that should make much difference here + var parEl = makeEl('div',{},'<div itemprop="foo" id="id1"></div><div itemscope itemref="id2 id1"><div itemprop="foo bar"></div></div><div itemprop="baz" id="id2"><div itemprop="qux"></div></div>'); + var testEl = parEl.childNodes[1]; + document.body.appendChild(parEl); + var fooLength = testEl.properties.namedItem('foo').length; + var barLength = testEl.properties.namedItem('bar').length; + var bazLength = testEl.properties.namedItem('baz').length; + var quxLength = testEl.properties.namedItem('qux').length; + var foo0 = testEl.properties.namedItem('foo')[0]; + var foo1 = testEl.properties.namedItem('foo')[1]; + var bar0 = testEl.properties.namedItem('bar')[0]; + var baz0 = testEl.properties.namedItem('baz')[0]; + var qux0 = testEl.properties.namedItem('qux')[0]; + document.body.removeChild(parEl); + assert_equals( fooLength, 2, 'foo length' ); + assert_equals( barLength, 1, 'bar length' ); + assert_equals( bazLength, 1, 'baz length' ); + assert_equals( quxLength, 1, 'qux length' ); + assert_equals( foo0, parEl.firstChild, 'foo 0' ); + assert_equals( foo1, testEl.firstChild, 'foo 1' ); + assert_equals( bar0, testEl.firstChild, 'bar 0' ); + assert_equals( baz0, parEl.lastChild, 'baz 0' ); + assert_equals( qux0, parEl.lastChild.firstChild, 'qux 0' ); +}, 'PropertyNodeList must see items added with itemref when attached to the document\'s DOM'); +test(function () { + var parEl = makeEl('div',{},'<div itemprop="foo" id="id1"></div><div itemscope itemref="id1 id2"><div itemprop="foo bar"></div></div><div itemprop="baz" id="id2"><div itemprop="qux"></div></div>'); + var testEl = parEl.childNodes[1]; + assert_equals( testEl.properties.namedItem('foo').length, 2, 'foo length' ); + assert_equals( testEl.properties.namedItem('bar').length, 1, 'bar length' ); + assert_equals( testEl.properties.namedItem('baz').length, 1, 'baz length' ); + assert_equals( testEl.properties.namedItem('qux').length, 1, 'qux length' ); + assert_equals( testEl.properties.namedItem('foo')[0], parEl.firstChild, 'foo 0' ); + assert_equals( testEl.properties.namedItem('foo')[1], testEl.firstChild, 'foo 1' ); + assert_equals( testEl.properties.namedItem('bar')[0], testEl.firstChild, 'bar 0' ); + assert_equals( testEl.properties.namedItem('baz')[0], parEl.lastChild, 'baz 0' ); + assert_equals( testEl.properties.namedItem('qux')[0], parEl.lastChild.firstChild, 'qux 0' ); +}, 'PropertyNodeList must see items added with itemref'); +test(function () { + //this one also tests the live object just in case - further ones will not always do this as its live status will already have been well established + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div>'); + var PNL = testEl.properties.namedItem('foo'); + testEl.removeAttribute('itemscope'); + assert_equals( testEl.properties.namedItem('foo').length, 0, 'removing attribute' ); + assert_equals( PNL.length, 0, 'removing attribute (live)' ); + assert_true( !testEl.properties['foo'], 'removing attribute []' ); + testEl.itemScope = true; + assert_equals( testEl.properties.namedItem('foo').length, 1, 'setting itemScope' ); + assert_equals( testEl.properties.namedItem('foo')[0], testEl.firstChild, 'property 0 after setting itemScope' ); + assert_equals( PNL.length, 1, 'setting itemScope (live)' ); + assert_equals( PNL[0], testEl.firstChild, 'property 0 after setting itemScope (live)' ); + assert_false( !testEl.properties['foo'], 'setting itemScope []' ); +}, 'PropertyNodeList must update when adding itemscope on the root'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div>'); + assert_equals( testEl.properties.namedItem('foo').length, 1, 'foo length (before test)' ); + assert_equals( testEl.properties.namedItem('foo')[0], testEl.firstChild, 'foo 0 (before test)' ); + assert_equals( testEl.properties.namedItem('bar').length, 0, 'bar length (before test)' ); + testEl.appendChild(makeEl('div',{itemprop:'bar'})); + assert_equals( testEl.properties.namedItem('foo').length, 1, 'foo length after adding a child' ); + assert_equals( testEl.properties.namedItem('foo')[0], testEl.firstChild, 'foo 0 after adding a child' ); + assert_equals( testEl.properties.namedItem('bar').length, 1, 'bar length after adding a child' ); + assert_equals( testEl.properties.namedItem('bar')[0], testEl.lastChild, 'bar 0 after adding a child' ); + testEl.lastChild.appendChild(makeEl('div',{itemprop:'foo'})); + assert_equals( testEl.properties.namedItem('foo').length, 2, 'foo length after adding a child with duplicated name' ); + assert_equals( testEl.properties.namedItem('foo')[0], testEl.firstChild, 'foo 0 after adding a child with duplicated name' ); + assert_equals( testEl.properties.namedItem('foo')[1], testEl.lastChild.firstChild, 'foo 1 after adding a child with duplicated name' ); + assert_equals( testEl.properties.namedItem('bar').length, 1, 'bar length after adding a child with duplicated name' ); + assert_equals( testEl.properties.namedItem('bar')[0], testEl.lastChild, 'bar 0 after adding a child with duplicated name' ); + testEl.lastChild.removeChild(testEl.lastChild.firstChild); + assert_equals( testEl.properties.namedItem('foo').length, 1, 'foo length after removing a child' ); + assert_equals( testEl.properties.namedItem('bar').length, 1, 'bar length after removing a child' ); +}, 'PropertyNodeList must update when adding property elements'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemprop="foo"></div>'); + var PNL = testEl.properties.namedItem('foo'); + assert_equals( PNL[0], testEl.firstChild, 'item 0 (before test)' ); + assert_equals( PNL[1], testEl.lastChild, 'item 1 (before test)' ); + testEl.appendChild(testEl.firstChild); + assert_equals( PNL[0], testEl.firstChild, 'item 0 (after test)' ); + assert_equals( PNL[1], testEl.lastChild, 'item 1 (after test)' ); +}, 'PropertyNodeList must update when re-ordering property elements'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div></div>'); + var PNLfoo = testEl.properties.namedItem('foo'), PNLbar = testEl.properties.namedItem('bar'); + assert_equals( PNLfoo.length, 1, 'foo length (before test)' ); + assert_equals( PNLbar.length, 0, 'bar length (before test)' ); + assert_equals( PNLfoo[0], testEl.firstChild, 'foo[0] (before test)' ); + testEl.lastChild.itemProp.toggle('bar'); + assert_equals( PNLfoo.length, 1, 'foo length after adding a token' ); + assert_equals( PNLbar.length, 1, 'bar length after adding a token' ); + assert_equals( PNLfoo[0], testEl.firstChild, 'foo[0] after adding a token' ); + assert_equals( PNLbar[0], testEl.lastChild, 'bar[0] after adding a token' ); + testEl.lastChild.itemProp.add('foo'); + assert_equals( PNLfoo.length, 2, 'foo length after adding a duplicated token' ); + assert_equals( PNLbar.length, 1, 'bar length after adding a duplicated token' ); + assert_equals( PNLfoo[0], testEl.firstChild, 'foo[0] after adding a duplicated token' ); + assert_equals( PNLfoo[1], testEl.lastChild, 'foo[1] after adding a duplicated token' ); + assert_equals( PNLbar[0], testEl.lastChild, 'bar[0] after adding a duplicated token' ); + testEl.lastChild.removeAttribute('itemprop'); + assert_equals( PNLfoo.length, 1, 'foo length after removing an attribute' ); + assert_equals( PNLbar.length, 0, 'bar length after removing an attribute' ); + assert_equals( PNLfoo[0], testEl.firstChild, 'foo[0] after removing an attribute' ); +}, 'PropertyNodeList must update when changing itemProp of children'); +test(function () { + var parEl = makeEl('div',{},'<div itemprop="foo"><div itemprop="bar"></div></div><div itemscope itemref="id1"></div>'); + var testEl = parEl.childNodes[1]; + var PNLfoo = testEl.properties.namedItem('foo'), PNLbar = testEl.properties.namedItem('bar'); + assert_equals( PNLfoo.length, 0, 'foo length (before test)' ); + assert_equals( PNLbar.length, 0, 'bar length (before test)' ); + parEl.firstChild.id = 'id1'; + assert_equals( PNLfoo.length, 1, 'foo length after id is created' ); + assert_equals( PNLbar.length, 1, 'bar length after id is created' ); + assert_equals( PNLfoo[0], parEl.firstChild, 'foo[0] after id is created' ); + assert_equals( PNLbar[0], parEl.firstChild.firstChild, 'bar[0] after id is created' ); + parEl.firstChild.removeAttribute('id'); + assert_equals( PNLfoo.length, 0, 'foo length after removing an attribute' ); + assert_equals( PNLbar.length, 0, 'bar length after removing an attribute' ); + document.body.appendChild(parEl); + var fooLength0 = PNLfoo.length; + var barLength0 = PNLbar.length; + parEl.firstChild.id = 'id1'; + var fooLength1 = PNLfoo.length; + var barLength1 = PNLbar.length; + var foo0 = PNLfoo[0]; + var bar0 = PNLbar[0]; + parEl.firstChild.removeAttribute('id'); + var fooLength2 = PNLfoo.length; + var barLength2 = PNLbar.length; + document.body.removeChild(parEl); + assert_equals( fooLength0, 0, 'foo length (before test) when appended to document' ); + assert_equals( barLength0, 0, 'bar length (before test) when appended to document' ); + assert_equals( fooLength1, 1, 'foo length after id is created when appended to document' ); + assert_equals( barLength1, 1, 'bar length after id is created when appended to document' ); + assert_equals( foo0, parEl.firstChild, 'foo[0] after id is created when appended to document' ); + assert_equals( bar0, parEl.firstChild.firstChild, 'bar[0] after id is created when appended to document' ); + assert_equals( fooLength2, 0, 'foo length after removing an attribute when appended to document' ); + assert_equals( barLength2, 0, 'bar length after removing an attribute when appended to document' ); +}, 'PropertyNodeList must update when changing id of referenced sibling'); +test(function () { + var parEl = makeEl('div',{},'<div itemprop="foo"><div itemprop="bar"></div></div><div itemscope itemref="id1"></div><div itemprop="baz" id="id1"></div>'); + var testEl = parEl.childNodes[1]; + var PNLfoo = testEl.properties.namedItem('foo'), PNLbar = testEl.properties.namedItem('bar'), PNLbaz = testEl.properties.namedItem('baz'); + assert_equals( PNLfoo.length, 0, 'foo length (before test)' ); + assert_equals( PNLbar.length, 0, 'bar length (before test)' ); + assert_equals( PNLbaz.length, 1, 'baz length (before test)' ); + assert_equals( PNLbaz[0], parEl.lastChild, 'baz[0] (before test)' ); + parEl.firstChild.id = 'id1'; + assert_equals( PNLfoo.length, 1, 'foo length after id is created' ); + assert_equals( PNLbar.length, 1, 'bar length after id is created' ); + assert_equals( PNLbaz.length, 0, 'baz length after id is created' ); + assert_equals( PNLfoo[0], parEl.firstChild, 'foo[0] after id is created' ); + assert_equals( PNLbar[0], parEl.firstChild.firstChild, 'bar[0] after id is created' ); + parEl.firstChild.removeAttribute('id'); + assert_equals( PNLfoo.length, 0, 'foo length after removing an attribute' ); + assert_equals( PNLbar.length, 0, 'bar length after removing an attribute' ); + assert_equals( PNLbaz.length, 1, 'baz length after removing an attribute' ); + assert_equals( PNLbaz[0], parEl.lastChild, 'baz[0] after removing an attribute' ); + document.body.appendChild(parEl); + var fooLength0 = PNLfoo.length; + var barLength0 = PNLbar.length; + var bazLength0 = PNLbaz.length; + var baz0 = PNLbaz[0]; + parEl.firstChild.id = 'id1'; + var fooLength1 = PNLfoo.length; + var barLength1 = PNLbar.length; + var bazLength1 = PNLbaz.length; + var foo0 = PNLfoo[0]; + var bar0 = PNLbar[0]; + parEl.firstChild.removeAttribute('id'); + var fooLength2 = PNLfoo.length; + var barLength2 = PNLbar.length; + var bazLength2 = PNLbaz.length; + var baz1 = PNLbaz[0]; + document.body.removeChild(parEl); + assert_equals( fooLength0, 0, 'foo length (before test) when appended to document' ); + assert_equals( barLength0, 0, 'bar length (before test) when appended to document' ); + assert_equals( bazLength0, 1, 'baz length (before test)' ); + assert_equals( baz0, parEl.lastChild, 'baz[0] (before test)' ); + assert_equals( fooLength1, 1, 'foo length after id is created when appended to document' ); + assert_equals( barLength1, 1, 'bar length after id is created when appended to document' ); + assert_equals( bazLength1, 0, 'baz length after id is created' ); + assert_equals( foo0, parEl.firstChild, 'foo[0] after id is created when appended to document' ); + assert_equals( bar0, parEl.firstChild.firstChild, 'bar[0] after id is created when appended to document' ); + assert_equals( fooLength2, 0, 'foo length after removing an attribute when appended to document' ); + assert_equals( barLength2, 0, 'bar length after removing an attribute when appended to document' ); + assert_equals( bazLength2, 1, 'baz length after removing an attribute' ); + assert_equals( baz0, parEl.lastChild, 'baz[0] after removing an attribute' ); +}, 'PropertyNodeList must update when changing duplicated id of referenced sibling'); +test(function () { + var parEl = makeEl('div',{},'<div id="id1" itemprop="foo"></div><div itemscope></div>'); + var testEl = parEl.childNodes[1]; + var PNL = testEl.properties.namedItem('foo'); + assert_equals( PNL.length, 0, 'length (before test)' ); + testEl.itemRef.toggle('id1'); + assert_equals( PNL.length, 1, 'length after itemref is changed' ); + assert_equals( PNL[0], parEl.firstChild, 'item 0 after itemref is changed' ); + testEl.removeAttribute('itemref'); + assert_equals( PNL.length, 0, 'length after itemref is removed' ); + assert_true( !PNL[0], 'item 0 after itemref is removed' ); + document.body.appendChild(parEl); + var length0 = PNL.length; + testEl.itemRef.toggle('id1'); + var length1 = PNL.length; + var foo0 = PNL[0]; + testEl.removeAttribute('itemref'); + var length2 = PNL.length; + var foo1 = PNL[0]; + document.body.removeChild(parEl); + assert_equals( length0, 0, 'length (before test) when appended to document' ); + assert_equals( length1, 1, 'length after itemref is changed when appended to document' ); + assert_equals( foo0, parEl.firstChild, 'item 0 after itemref is changed when appended to document' ); + assert_equals( length2, 0, 'length after itemref is removed when appended to document' ); + assert_true( !foo1, 'item 0 after itemref is removed when appended to document' ); +}, 'PropertyNodeList must update when changing itemref to point to an element'); +test(function () { + var parEl = makeEl('div',{},'<div id="id1"><div></div></div><div itemscope itemref="id1"></div>'); + var testEl = parEl.childNodes[1]; + var PNLfoo = testEl.properties.namedItem('foo'), PNLbar = testEl.properties.namedItem('bar'); + assert_equals( PNLfoo.length, 0, 'foo length (before test)' ); + assert_equals( PNLbar.length, 0, 'bar length (before test)' ); + parEl.firstChild.appendChild(makeEl('div',{itemprop:'foo'})); + assert_equals( PNLfoo.length, 1, 'foo length after a referenced element is added' ); + assert_equals( PNLbar.length, 0, 'bar length after a referenced element is added' ); + assert_equals( PNLfoo.item(0), parEl.firstChild.lastChild, 'foo 0 after a referenced element is added' ); //uses item just for the fun of it + parEl.firstChild.firstChild.itemProp.toggle('bar'); + assert_equals( PNLfoo.length, 1, 'foo length after a referenced itemprop is changed' ); + assert_equals( PNLbar.length, 1, 'bar length after a referenced itemprop is changed' ); + assert_equals( PNLfoo[0], parEl.firstChild.lastChild, 'foo 0 after a referenced element is added' ); + assert_equals( PNLbar[0], parEl.firstChild.firstChild, 'bar 0 after a referenced element is added' ); + parEl.firstChild.removeChild(parEl.firstChild.firstChild); + assert_equals( PNLfoo.length, 1, 'foo length after a referenced element is removed' ); + assert_equals( PNLbar.length, 0, 'bar length after a referenced element is removed' ); + assert_equals( PNLfoo[0], parEl.firstChild.firstChild, 'foo 0 after a referenced element is removed' ); + assert_true( !PNLbar[0], 'bar 0 after a referenced element is removed' ); + parEl.innerHTML = '<div id="id1"><div></div></div><div itemscope itemref="id1"></div>'; + testEl = parEl.childNodes[1]; + PNLfoo = testEl.properties.namedItem('foo'); + PNLbar = testEl.properties.namedItem('bar'); + document.body.appendChild(parEl); + var step1fooLength = PNLfoo.length; + var step1barLength = PNLbar.length; + parEl.firstChild.appendChild(makeEl('div',{itemprop:'foo'})); + var step2fooLength = PNLfoo.length; + var step2barLength = PNLbar.length; + var step2foo0 = PNLfoo.item(0); //uses item just for the fun of it + var step2fooExpected = parEl.firstChild.lastChild; + parEl.firstChild.firstChild.itemProp.toggle('bar'); + var step3fooLength = PNLfoo.length; + var step3barLength = PNLbar.length; + var step3foo0 = PNLfoo[0]; + var step3bar0 = PNLbar[0]; + var step3fooExpected = parEl.firstChild.lastChild; + var step3barExpected = parEl.firstChild.firstChild; + parEl.firstChild.removeChild(parEl.firstChild.firstChild); + var step4fooLength = PNLfoo.length; + var step4barLength = PNLbar.length; + var step4foo0 = PNLfoo[0]; + var step4bar0 = PNLbar[0]; + var step4fooExpected = parEl.firstChild.firstChild; + document.body.removeChild(parEl); + assert_equals( step1fooLength, 0, 'foo length (before test) when appended to document' ); + assert_equals( step1barLength, 0, 'bar length (before test) when appended to document' ); + assert_equals( step2fooLength, 1, 'foo length after a referenced element is added when appended to document' ); + assert_equals( step2barLength, 0, 'bar length after a referenced element is added when appended to document' ); + assert_equals( step2foo0, step2fooExpected, 'foo 0 after a referenced element is added when appended to document' ); + assert_equals( step3fooLength, 1, 'foo length after a referenced itemprop is changed when appended to document' ); + assert_equals( step3barLength, 1, 'bar length after a referenced itemprop is changed when appended to document' ); + assert_equals( step3foo0, step3fooExpected, 'foo 0 after a referenced element is added when appended to document' ); + assert_equals( step3bar0, step3barExpected, 'bar 0 after a referenced element is added when appended to document' ); + assert_equals( step4fooLength, 1, 'foo length after a referenced element is removed when appended to document' ); + assert_equals( step4barLength, 0, 'bar length after a referenced element is removed when appended to document' ); + assert_equals( step4foo0, step4fooExpected, 'foo 0 after a referenced element is removed when appended to document' ); + assert_true( !step4bar0, 'bar 0 after a referenced element is removed when appended to document' ); +}, 'PropertyNodeList must update when changing children of elements referenced through itemref'); +test(function () { + var parEl = makeEl('div',{},'<div id="id1" itemprop="foo"></div><div itemscope itemref="id1"><div itemprop="foo"></div></div>'); + var testEl = parEl.childNodes[1]; + var PNL = testEl.properties.namedItem('foo'); + assert_equals( PNL.length, 2, 'length (before test)' ); + assert_equals( PNL[0], parEl.firstChild, 'item 0 (before test)' ); + assert_equals( PNL[1], testEl.firstChild, 'item 1 (before test)' ); + document.body.appendChild(testEl); + var step1length = PNL.length; + var step1prop0 = PNL[0]; + var step1prop1 = PNL[1]; + parEl.appendChild(testEl); + assert_equals( step1length, 1, 'length after changing parent' ); + assert_equals( step1prop0, testEl.firstChild, 'item 0 after changing parent' ); + assert_true( !step1prop1, 'item 1 after changing parent' ); + assert_equals( PNL.length, 2, 'length after re-parenting' ); + assert_equals( PNL[0], parEl.firstChild, 'item 0 after re-parenting' ); + assert_equals( PNL[1], testEl.firstChild, 'item 1 after re-parenting' ); + document.body.appendChild(parEl); + var step2length = PNL.length; + var step2prop0 = PNL[0]; + var step2prop1 = PNL[1]; + document.createElement('div').appendChild(testEl); + var step3length = PNL.length; + var step3prop0 = PNL[0]; + var step3prop1 = PNL[1]; + parEl.appendChild(testEl); + var step4length = PNL.length; + var step4prop0 = PNL[0]; + var step4prop1 = PNL[1]; + document.body.removeChild(parEl); + assert_equals( step2length, 2, 'length (before test) when appended to document' ); + assert_equals( step2prop0, parEl.firstChild, 'item 0 (before test) when appended to document' ); + assert_equals( step2prop1, testEl.firstChild, 'item 1 (before test) when appended to document' ); + assert_equals( step3length, 1, 'length after changing parent when appended to document' ); + assert_equals( step3prop0, testEl.firstChild, 'item 0 after changing parent when appended to document' ); + assert_true( !step3prop1, 'item 1 after changing parent when appended to document' ); + assert_equals( step4length, 2, 'length after re-parenting when appended to document' ); + assert_equals( step4prop0, parEl.firstChild, 'item 0 after re-parenting when appended to document' ); + assert_equals( step4prop1, testEl.firstChild, 'item 1 after re-parenting when appended to document' ); +}, 'PropertyNodeList must update when appending elements with itemref to different parents'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div><div itemprop="foo"></div></div>'); + var PNL = testEl.properties.namedItem('foo'); + assert_equals( PNL.length, 1, 'length (before test)' ); + assert_equals( PNL[0], testEl.firstChild.firstChild, 'foo 0 (before test)' ); + testEl.firstChild.itemScope = true; + assert_equals( PNL.length, 0, 'length after setting itemscope' ); + assert_true( !PNL[0], 'foo 0 after setting itemscope' ); + testEl.firstChild.removeAttribute('itemscope'); + assert_equals( PNL.length, 1, 'length after removing itemscope attribute' ); + assert_equals( PNL[0], testEl.firstChild.firstChild, 'foo 0 after removing itemscope attribute' ); +}, 'PropertyNodeList must update when changing itemscope of children'); +//PropertyNodeList.getValues +test(function () { + var valuesArray = makeEl('div',{}).properties.namedItem('foo').getValues(); + assert_true( valuesArray instanceof Array, 'instanceof test' ); + Array.prototype.customProp = true; + assert_true( valuesArray.customProp, 'inheritance test' ); +}, 'getValues must return an array'); +test(function () { + var testEl = makeEl('div',{}); + var props = testEl.properties.namedItem('foo'); + assert_not_equals( props.getValues(), props.getValues() ); +}, 'getValues must always return a newly constructed array'); +test(function () { + var parEl = makeEl('div',{},'<div id="id1"></div>'); + var testEl = makeEl('div',{itemscope:'itemscope',itemref:'id1 id2'}); + parEl.appendChild(testEl); + testEl.appendChild(makeEl('meta',{itemprop:'foo',content:'test'})); + testEl.appendChild(makeEl('audio',{itemprop:'foo',src:'http://example.org/'},'contained text')); + testEl.appendChild(makeEl('embed',{itemprop:'foo',src:'http://example.org/'})); + testEl.appendChild(makeEl('iframe',{itemprop:'foo',src:'http://example.org/'},'contained text')); + testEl.appendChild(makeEl('img',{itemprop:'foo',src:'http://example.org/'})); + testEl.appendChild(makeEl('source',{itemprop:'foo',src:'http://example.org/'})); + testEl.appendChild(makeEl('track',{itemprop:'foo',src:'http://example.org/'})); + testEl.appendChild(makeEl('video',{itemprop:'foo',src:'http://example.org/'},'contained text')); + testEl.appendChild(makeEl('a',{itemprop:'foo',href:'http://example.org/'},'contained text')); + testEl.appendChild(makeEl('area',{itemprop:'foo',href:'http://example.org/'})); + testEl.appendChild(makeEl('link',{itemprop:'foo',href:'http://example.org/'})); + testEl.appendChild(makeEl('object',{itemprop:'foo',data:'http://example.org/'},'contained text')); + parEl.appendChild(makeEl('time',{itemprop:'foo',id:'id2'},'te <span itemprop="foo" itemscope>st</span> ing')); + testEl.appendChild(makeEl('time',{itemprop:'foo',datetime:'test'},'te <span itemprop="foo" itemscope>st</span> ing')); + parEl.firstChild.appendChild(makeEl('div',{itemprop:'foo'},'te <span itemprop="foo" itemscope>st</span> ing')); + testEl.appendChild(makeEl('madeuponthespot',{itemprop:'foo'},'te <span itemprop="foo" itemscope>st</span> ing')); + var PNL = testEl.properties.namedItem('foo'); + var valuesArray = PNL.getValues(); + for( var i = 0; i < PNL.length; i++ ) { + assert_equals( valuesArray[i], PNL[i].itemValue, 'property index ' + i + ', tag ' + PNL[i].tagName ); + } + assert_equals( valuesArray.length, 20, 'length' ); +}, 'getValues array must contain the same item values as itemValue would return for the given properties'); +test(function () { + var parEl = makeEl('div',{},'<div id="id1"></div>'); + var testEl = makeEl('div',{itemscope:'itemscope',itemref:'id1 id2'}); + parEl.appendChild(testEl); + testEl.appendChild(makeEl('meta',{itemprop:'foo',content:'test'})); + testEl.appendChild(makeEl('audio',{itemprop:'foo',src:'http://example.org/'},'contained text')); + testEl.appendChild(makeEl('embed',{itemprop:'foo',src:'http://example.org/'})); + testEl.appendChild(makeEl('iframe',{itemprop:'foo',src:'http://example.org/'},'contained text')); + testEl.appendChild(makeEl('img',{itemprop:'foo',src:'http://example.org/'})); + testEl.appendChild(makeEl('source',{itemprop:'foo',src:'http://example.org/'})); + testEl.appendChild(makeEl('track',{itemprop:'foo',src:'http://example.org/'})); + testEl.appendChild(makeEl('video',{itemprop:'foo',src:'http://example.org/'},'contained text')); + testEl.appendChild(makeEl('a',{itemprop:'foo',href:'http://example.org/'},'contained text')); + testEl.appendChild(makeEl('area',{itemprop:'foo',href:'http://example.org/'})); + testEl.appendChild(makeEl('link',{itemprop:'foo',href:'http://example.org/'})); + testEl.appendChild(makeEl('object',{itemprop:'foo',data:'http://example.org/'},'contained text')); + parEl.appendChild(makeEl('time',{itemprop:'foo',id:'id2'},'te <span itemprop="foo" itemscope>st</span> ing')); + testEl.appendChild(makeEl('time',{itemprop:'foo',datetime:'test'},'te <span itemprop="foo" itemscope>st</span> ing')); + parEl.firstChild.appendChild(makeEl('div',{itemprop:'foo'},'te <span itemprop="foo" itemscope>st</span> ing')); + testEl.appendChild(makeEl('madeuponthespot',{itemprop:'foo'},'te <span itemprop="foo" itemscope>st</span> ing')); + var PNL = testEl.properties.namedItem('foo'); + var valuesArray = PNL.getValues(); + var staticArray = []; + for( var i = 0; i < PNL.length; i++ ) { + staticArray[i] = PNL[i].itemValue; + } + testEl.innerHTML = ''; + parEl.firstChild.firstChild.childNodes[1].itemScope = false; + assert_equals( valuesArray.length, staticArray.length, 'length after modification' ); + for( var j = 0; j < staticArray.length; j++ ) { + assert_equals( valuesArray[j], staticArray[j], 'property index ' + j ); + } + assert_equals( valuesArray[1], parEl.firstChild.firstChild.childNodes[1], 'retaining pointer after modification' ); + staticArray = null; + parEl.firstChild.firstChild.innerHTML = ''; + assert_equals( valuesArray[1] && valuesArray[1].nodeType, 1, 'retaining pointer after removal' ); +}, 'getValues array must not be live'); +//names +test(function () { + assert_equals( typeof makeEl('div',{}).properties.names, 'object' ); +}, 'the names property must be an object'); +test(function () { + var testEl = makeEl('div',{}); + assert_true( testEl.properties.names instanceof DOMStringList, 'instanceof DOMStringList' ); + DOMStringList.prototype.stringCustomProperty = true; + assert_true( testEl.properties.names.stringCustomProperty, 'inheritance from DOMStringList' ); +}, 'the names property must implement DOMStringList'); +test(function () { + var failed = false, elem = makeEl('div',{itemscope:'itemscope'}), realList = elem.properties.names; + try { + elem.properties.names = ''; + } catch(e) { + failed = e; + } + assert_equals(elem.properties.names,realList); + assert_false(failed,'an error was thrown'); +}, 'the names property must be read-only'); +test(function () { + var testEl = makeEl('div',{}); + assert_equals( testEl.properties.names, testEl.properties.names ); +}, 'the names property must always reference the same object'); +test(function () { + var testEl = makeEl('div',{}); + assert_equals( testEl.properties.names.item(0), null, 'item(0)' ); + assert_equals( testEl.properties.names.item(-1), null, 'item(-1)' ); +}, 'names.item() must return null for out of range indexes'); +test(function () { + var testEl = makeEl('div',{}); + assert_equals( testEl.properties.names[0], window.undefined, '[0]' ); + assert_equals( testEl.properties.names[-1], window.undefined, '[-1]' ); +}, 'names[index] must return undefined for out of range indexes'); +test(function () { + var testEl = makeEl('div',{},'<div itemprop="foo">bar</div>'); + assert_equals( testEl.properties.names.length, 0, 'length' ); + assert_true( !testEl.properties.names.item(0), 'item(0)' ); + assert_true( !testEl.properties.names[0], '[0]' ); + assert_false( testEl.properties.names.contains('foo'), 'contains' ); +}, 'the names collection must be empty if the element does not have an itemscope property'); +test(function () { + var testEl = makeEl('div',{},'<div itemprop="foo">bar</div>'); + testEl.itemScope = true; + assert_equals( testEl.properties.names.length, 1, 'length' ); + assert_equals( testEl.properties.names.item(0), 'foo', 'item(0)' ); + assert_equals( testEl.properties.names[0], 'foo', '[0]' ); + assert_true( testEl.properties.names.contains('foo'), 'contains' ); +}, 'the names collection must become populated if the element is given an itemscope property'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo">bar</div>'); + testEl.itemScope = false; + assert_equals( testEl.properties.names.length, 0, 'length' ); + assert_true( !testEl.properties.names.item(0), 'item(0)' ); + assert_true( !testEl.properties.names[0], '[0]' ); + assert_false( testEl.properties.names.contains('foo'), 'contains' ); +}, 'the names collection must become empty if the element\'s itemscope property is removed'); +test(function () { + var testEl = makeEl('div',{},'<div itemprop="foo">bar</div>'); + testEl.properties.names.item = 'test'; + testEl.properties.names.contains = 'test'; + assert_equals( testEl.properties.names.item, 'test', 'item' ); + assert_equals( testEl.properties.names.contains, 'test', 'contains' ); +}, 'the names.item and names.contains methods should be overwriteable'); +test(function () { + var testEl = makeEl('div',{},'<div itemprop="foo">bar</div>'); + testEl.properties.names.localCustomProperty = 'test'; + assert_equals( testEl.properties.names.localCustomProperty, 'test' ); +}, 'the names.customProperty should be writeable'); +test(function () { + //WebIDL and ECMAScript 5 - a readonly property has a getter but not a setter + //ES5 makes [[Put]] fail but not throw + var failed = false; + var elem = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo">bar</div>'); + try { + elem.properties.names.length = 0; + } catch(e) { + failed = e; + } + assert_equals(elem.properties.names.length,1); + assert_false(failed,'an error was thrown'); +}, 'names.length must be read-only'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemprop="bar"><div itemprop="foo"></div><div itemprop="foo"></div></div><div itemprop="baz qux"></div>'); + assert_equals( testEl.properties.names.length, 4 ); +}, 'names.length must be the total number of property names'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemprop="bar"><div itemprop="foo"></div><div itemprop="foo"></div></div><div itemprop="baz \t\r\n\fqux"></div>'); + assert_equals( testEl.properties.names.item(0), 'foo', 'item(0)' ); + assert_equals( testEl.properties.names.item(1), 'bar', 'item(1)' ); + assert_equals( testEl.properties.names.item(2), 'baz', 'item(2)' ); + assert_equals( testEl.properties.names.item(3), 'qux', 'item(3)' ); + assert_equals( testEl.properties.names[0], 'foo', '[0]' ); + assert_equals( testEl.properties.names[1], 'bar', '[1]' ); + assert_equals( testEl.properties.names[2], 'baz', '[2]' ); + assert_equals( testEl.properties.names[3], 'qux', '[3]' ); +}, 'names.item must give each property name in tree order'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemprop="bar BAR bar"><div itemprop="FOO"></div><div itemprop="foo"></div></div><div itemprop="baz qux"></div>'); + assert_equals( testEl.properties.names.length, 6, 'length' ); + assert_equals( testEl.properties.names.item(0), 'foo', 'item(0)' ); + assert_equals( testEl.properties.names.item(1), 'bar', 'item(1)' ); + assert_equals( testEl.properties.names.item(2), 'BAR', 'item(2)' ); + assert_equals( testEl.properties.names.item(3), 'FOO', 'item(3)' ); + assert_equals( testEl.properties.names.item(4), 'baz', 'item(4)' ); + assert_equals( testEl.properties.names.item(5), 'qux', 'item(5)' ); + assert_equals( testEl.properties.names[0], 'foo', '[0]' ); + assert_equals( testEl.properties.names[1], 'bar', '[1]' ); + assert_equals( testEl.properties.names[2], 'BAR', '[2]' ); + assert_equals( testEl.properties.names[3], 'FOO', '[3]' ); + assert_equals( testEl.properties.names[4], 'baz', '[4]' ); + assert_equals( testEl.properties.names[5], 'qux', '[5]' ); +}, 'names must be case sensitive'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemprop="bar"><div itemprop="FOO"></div><div itemprop="foo"></div></div><div itemprop="baz qux"></div>'); + assert_true( testEl.properties.names.contains('foo'), 'foo' ); + assert_true( testEl.properties.names.contains('FOO'), 'FOO' ); + assert_true( testEl.properties.names.contains('bar'), 'bar' ); + assert_false( testEl.properties.names.contains('BAR'), 'BAR' ); + assert_true( testEl.properties.names.contains('baz'), 'baz' ); + assert_true( testEl.properties.names.contains('qux'), 'qux' ); + assert_false( testEl.properties.names.contains('madeup'), 'madeup' ); +}, 'names.contains must return boolean if the name exists'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="1"></div>'); + assert_equals( testEl.properties.names.item('1'), null ); +}, 'names.item must cast to number'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemprop="1"></div>'); + assert_true( testEl.properties.names.contains({ valueOf: function () { return 2; }, toString: function () { return 'foo'; } }), 'object' ); + assert_true( testEl.properties.names.contains(1), 'number' ); +}, 'names.contains must cast to string'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemprop="bar"></div>'); + var namesList = testEl.properties.names; + testEl.innerHTML = '<div itemprop="baz"></div>'; + assert_equals( testEl.properties.names.length, 1, 'length' ); + assert_equals( testEl.properties.names[0], 'baz', '[0]' ); +}, 'the names collection must be live'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="|§!"#¤%&/()=?`\\@£${[]}´€^¨~\'*,;.:-_<>åøæÅØÆ"></div>'); + assert_equals( testEl.properties.names[0], '|§!"#¤%&/()=?`\\@£${[]}´€^¨~\'*,;.:-_<>åøæÅØÆ' ); +}, 'names must reflect property names containing special characters'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemscope><div itemprop="bar"></div></div>'); + assert_equals( testEl.properties.names.length, 1, 'length' ); + assert_equals( testEl.properties.names[0], 'foo', '[0]' ); + assert_true( testEl.properties.names.contains('foo'), 'contains(foo)' ); + assert_false( testEl.properties.names.contains('bar'), 'contains(bar)' ); +}, 'names must ignore properties of nested items'); +test(function () { + //note, itemref ordering is reversed compared with the next test to catch failed sorting algorithms + var parEl = makeEl('div',{},'<div itemprop="foo" id="id1"></div><div itemscope itemref="id2 id1"><div itemprop="bar"></div></div><div itemprop="baz" id="id2"><div itemprop="qux"></div></div>'); + var testEl = parEl.childNodes[1]; + document.body.appendChild(parEl); + var length = testEl.properties.names.length; + var names0 = testEl.properties.names[0]; + var names1 = testEl.properties.names[1]; + var names2 = testEl.properties.names[2]; + var names3 = testEl.properties.names[3]; + document.body.removeChild(parEl); + assert_equals( length, 4, 'length' ); + assert_equals( names0, 'foo', 'names[0]' ); + assert_equals( names1, 'bar', 'names[1]' ); + assert_equals( names2, 'baz', 'names[2]' ); + assert_equals( names3, 'qux', 'names[3]' ); +}, 'names must see items added with itemref when attached to the document\'s DOM'); +test(function () { + var parEl = makeEl('div',{},'<div itemprop="foo" id="id1"></div><div itemscope itemref="id1 id2"><div itemprop="bar"></div></div><div itemprop="baz" id="id2"><div itemprop="qux"></div></div>'); + var testEl = parEl.childNodes[1]; + assert_equals( testEl.properties.names.length, 4, 'length' ); + assert_equals( testEl.properties.names[0], 'foo', 'names[0]' ); + assert_equals( testEl.properties.names[1], 'bar', 'names[1]' ); + assert_equals( testEl.properties.names[2], 'baz', 'names[2]' ); + assert_equals( testEl.properties.names[3], 'qux', 'names[3]' ); +}, 'names must see items added with itemref'); +test(function () { + //this one also tests the live object just in case - further ones will not always do this as its live status will already have been well established + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div>'); + var DSL = testEl.properties.names; + testEl.removeAttribute('itemscope'); + assert_equals( testEl.properties.names.length, 0, 'removing attribute' ); + assert_equals( DSL.length, 0, 'removing attribute (live)' ); + assert_true( !testEl.properties.names[0], 'removing attribute [0]' ); + assert_true( !DSL[0], 'removing attribute [0] (live)' ); + testEl.itemScope = true; + assert_equals( testEl.properties.names.length, 1, 'setting itemScope' ); + assert_equals( DSL.length, 1, 'setting itemScope (live)' ); + assert_equals( testEl.properties.names[0], 'foo', 'names[0] after setting itemScope' ); + assert_equals( DSL[0], 'foo', 'names[0] after setting itemScope (live)' ); +}, 'names must update when adding itemscope on the root'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div>'); + assert_equals( testEl.properties.names.length, 1, 'length (before test)' ); + assert_equals( testEl.properties.names[0], 'foo', 'item 0 (before test)' ); + testEl.appendChild(makeEl('div',{itemprop:'bar'})); + assert_equals( testEl.properties.names.length, 2, 'length after adding a child' ); + assert_equals( testEl.properties.names[0], 'foo', 'item 0 after adding a child' ); + assert_equals( testEl.properties.names[1], 'bar', 'item 1 after adding a child' ); + testEl.lastChild.appendChild(makeEl('div',{itemprop:'foo'})); + assert_equals( testEl.properties.names.length, 2, 'foo length after adding a child with duplicated name' ); + assert_equals( testEl.properties.names[0], 'foo', 'item 0 after adding a child with duplicated name' ); + assert_equals( testEl.properties.names[1], 'bar', 'item 1 after adding a child with duplicated name' ); + testEl.removeChild(testEl.lastChild); + assert_equals( testEl.properties.names.length, 1, 'length after removing a child' ); + assert_equals( testEl.properties.names[0], 'foo', 'item 0 after removing a child' ); +}, 'names must update when adding property elements'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemprop="bar"></div>'); + var DSL = testEl.properties.names; + assert_equals( DSL[0], 'foo', 'item 0 (before test)' ); + assert_equals( DSL[1], 'bar', 'item 1 (before test)' ); + testEl.appendChild(testEl.firstChild); + assert_equals( DSL[0], 'bar', 'item 0 (after test)' ); + assert_equals( DSL[1], 'foo', 'item 1 (after test)' ); +}, 'names must update when re-ordering property elements'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div></div>'); + var DSL = testEl.properties.names; + assert_equals( DSL.length, 1, 'length (before test)' ); + assert_equals( DSL[0], 'foo', 'item 0 (before test)' ); + testEl.lastChild.itemProp.toggle('bar'); + assert_equals( DSL.length, 2, 'length after adding a token' ); + assert_equals( DSL[0], 'foo', 'item 0 after adding a token' ); + assert_equals( DSL[1], 'bar', 'item 1 after adding a token' ); + testEl.lastChild.itemProp.add('foo'); + assert_equals( DSL.length, 2, 'length after adding a duplicated token' ); + assert_equals( DSL[0], 'foo', 'item 0 after adding a duplicated token' ); + assert_equals( DSL[1], 'bar', 'item 1 after adding a duplicated token' ); + testEl.lastChild.removeAttribute('itemprop'); + assert_equals( DSL.length, 1, 'length after removing an attribute' ); + assert_equals( DSL[0], 'foo', 'item 0 after removing an attribute' ); + testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div itemprop="bar"></div><div itemprop="foo"></div>'); + DSL = testEl.properties.names; + assert_equals( DSL.length, 2, 'length (before second test)' ); + assert_equals( DSL[0], 'foo', 'item 0 (before second test)' ); + assert_equals( DSL[1], 'bar', 'item 1 (before second test)' ); + testEl.firstChild.removeAttribute('itemprop'); + assert_equals( DSL.length, 2, 'length after removing attribute of first item' ); + assert_equals( DSL[0], 'bar', 'item 0 after removing attribute of first item' ); + assert_equals( DSL[1], 'foo', 'item 1 after removing attribute of first item' ); + testEl.firstChild.itemProp.add('foo'); + assert_equals( DSL.length, 2, 'length after adding duplicated token to first item' ); + assert_equals( DSL[0], 'foo', 'item 0 after adding duplicated token to first item' ); + assert_equals( DSL[1], 'bar', 'item 1 after adding duplicated token to first item' ); + testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo bar"></div>'); + DSL = testEl.properties.names; + assert_equals( DSL.length, 2, 'length (before third test)' ); + assert_equals( DSL[0], 'foo', 'item 0 (before third test)' ); + assert_equals( DSL[1], 'bar', 'item 1 (before third test)' ); + testEl.firstChild.itemProp.toggle('foo'); + testEl.firstChild.itemProp.toggle('foo'); + assert_equals( DSL.length, 2, 'length after swapping tokens' ); + assert_equals( DSL[0], 'bar', 'item 0 after swapping tokens' ); + assert_equals( DSL[1], 'foo', 'item 1 after swapping tokens' ); +}, 'names must update when changing itemProp of children'); +test(function () { + var parEl = makeEl('div',{},'<div itemprop="foo"><div itemprop="bar"></div></div><div itemscope itemref="id1"></div>'); + var testEl = parEl.childNodes[1]; + var DSL = testEl.properties.names; + assert_equals( DSL.length, 0, 'length (before test)' ); + parEl.firstChild.id = 'id1'; + assert_equals( DSL.length, 2, 'length after id is created' ); + assert_equals( DSL[0], 'foo', 'item 0 after id is created' ); + assert_equals( DSL[1], 'bar', 'item 1 after id is created' ); + parEl.firstChild.removeAttribute('id'); + assert_equals( DSL.length, 0, 'length after removing an attribute' ); + document.body.appendChild(parEl); + var step1length = DSL.length; + parEl.firstChild.id = 'id1'; + var step2length = DSL.length; + var step2item0 = DSL[0]; + var step2item1 = DSL[1]; + parEl.firstChild.removeAttribute('id'); + var step3length = DSL.length; + document.body.removeChild(parEl); + assert_equals( step1length, 0, 'length (before test) when appended to document' ); + assert_equals( step2length, 2, 'length after id is created when appended to document' ); + assert_equals( step2item0, 'foo', 'item 0 after id is created when appended to document' ); + assert_equals( step2item1, 'bar', 'item 1 after id is created when appended to document' ); + assert_equals( step3length, 0, 'length after removing an attribute when appended to document' ); +}, 'names must update when changing id of referenced sibling when appended to document'); +test(function () { + var parEl = makeEl('div',{},'<div itemprop="foo"><div itemprop="bar"></div></div><div itemscope itemref="id1"></div><div itemprop="baz" id="id1"></div>'); + var testEl = parEl.childNodes[1]; + var DSL = testEl.properties.names; + assert_equals( DSL.length, 1, 'length (before test)' ); + assert_equals( DSL[0], 'baz', 'item 0 (before test)' ); + parEl.firstChild.id = 'id1'; + assert_equals( DSL.length, 2, 'length after id is created' ); + assert_equals( DSL[0], 'foo', 'item 0 after id is created' ); + assert_equals( DSL[1], 'bar', 'item 1 after id is created' ); + parEl.firstChild.removeAttribute('id'); + assert_equals( DSL.length, 1, 'length after removing an attribute' ); + assert_equals( DSL[0], 'baz', 'item 0 after removing an attribute' ); + document.body.appendChild(parEl); + var step1length = DSL.length; + var step1item0 = DSL[0]; + parEl.firstChild.id = 'id1'; + var step2length = DSL.length; + var step2item0 = DSL[0]; + var step2item1 = DSL[1]; + parEl.firstChild.removeAttribute('id'); + var step3length = DSL.length; + var step3item0 = DSL[0]; + document.body.removeChild(parEl); + assert_equals( step1length, 1, 'length (before test)' ); + assert_equals( step1item0, 'baz', 'item 0 (before test)' ); + assert_equals( step2length, 2, 'length after id is created' ); + assert_equals( step2item0, 'foo', 'item 0 after id is created' ); + assert_equals( step2item1, 'bar', 'item 1 after id is created' ); + assert_equals( step3length, 1, 'length after removing an attribute' ); + assert_equals( step3item0, 'baz', 'item 0 after removing an attribute' ); +}, 'names must update when changing duplicated id of referenced sibling'); +test(function () { + var parEl = makeEl('div',{},'<div id="id1" itemprop="foo"></div><div itemscope></div>'); + var testEl = parEl.childNodes[1]; + var DSL = testEl.properties.names; + assert_equals( DSL.length, 0, 'length (before test)' ); + testEl.itemRef.toggle('id1'); + assert_equals( DSL.length, 1, 'length after itemref is changed' ); + assert_equals( DSL[0], 'foo', 'item 0 after itemref is changed' ); + testEl.removeAttribute('itemref'); + assert_equals( DSL.length, 0, 'length after itemref is removed' ); + assert_true( !DSL[0], 'item 0 after itemref is removed' ); + document.body.appendChild(parEl); + var step1length = DSL.length; + testEl.itemRef.toggle('id1'); + var step2length = DSL.length; + var step2item = DSL[0]; + testEl.removeAttribute('itemref'); + var step3length = DSL.length; + var step3item = DSL[0]; + document.body.removeChild(parEl); + assert_equals( step1length, 0, 'length (before test)' ); + assert_equals( step2length, 1, 'length after itemref is changed' ); + assert_equals( step2item, 'foo', 'item 0 after itemref is changed' ); + assert_equals( step3length, 0, 'length after itemref is removed' ); + assert_true( !step3item, 'item 0 after itemref is removed' ); +}, 'names must update when changing itemref to point to an element'); +test(function () { + var parEl = makeEl('div',{},'<div id="id1"><div></div></div><div itemscope itemref="id1"></div>'); + var testEl = parEl.childNodes[1]; + var DSL = testEl.properties.names; + assert_equals( DSL.length, 0, 'length (before test)' ); + parEl.firstChild.appendChild(makeEl('div',{itemprop:'foo'})); + assert_equals( DSL.length, 1, 'length after a referenced element is added' ); + assert_equals( DSL.item(0), 'foo', 'item 0 after a referenced element is added' ); //uses item just for the fun of it + parEl.firstChild.firstChild.itemProp.toggle('bar'); + assert_equals( DSL.length, 2, 'length after a referenced itemprop is changed' ); + assert_equals( DSL[0], 'bar', 'item 0 after a referenced element is added' ); + assert_equals( DSL[1], 'foo', 'item 1 after a referenced element is added' ); + parEl.firstChild.removeChild(parEl.firstChild.firstChild); + assert_equals( DSL.length, 1, 'length after a referenced element is removed' ); + assert_equals( DSL[0], 'foo', 'item 0 after a referenced element is removed' ); + parEl.innerHTML = '<div id="id1"><div></div></div><div itemscope itemref="id1"></div>'; + testEl = parEl.childNodes[1]; + DSL = testEl.properties.names; + document.body.appendChild(parEl); + var step1length = DSL.length; + parEl.firstChild.appendChild(makeEl('div',{itemprop:'foo'})); + var step2length = DSL.length; + var step2item0 = DSL.item(0); //uses item just for the fun of it + parEl.firstChild.firstChild.itemProp.toggle('bar'); + var step3length = DSL.length; + var step3item0 = DSL[0]; + var step3item1 = DSL[1]; + parEl.firstChild.removeChild(parEl.firstChild.firstChild); + var step4length = DSL.length; + var step4item0 = DSL[0]; + document.body.removeChild(parEl); + assert_equals( step1length, 0, 'length (before test)' ); + parEl.firstChild.appendChild(makeEl('div',{itemprop:'foo'})); + assert_equals( step2length, 1, 'length after a referenced element is added' ); + assert_equals( step2item0, 'foo', 'item 0 after a referenced element is added' ); //uses item just for the fun of it + parEl.firstChild.firstChild.itemProp.toggle('bar'); + assert_equals( step3length, 2, 'length after a referenced itemprop is changed' ); + assert_equals( step3item0, 'bar', 'item 0 after a referenced element is added' ); + assert_equals( step3item1, 'foo', 'item 1 after a referenced element is added' ); + parEl.firstChild.removeChild(parEl.firstChild.firstChild); + assert_equals( step4length, 1, 'length after a referenced element is removed' ); + assert_equals( step4item0, 'foo', 'item 0 after a referenced element is removed' ); +}, 'names must update when changing children of elements referenced through itemref'); +test(function () { + var parEl = makeEl('div',{},'<div id="id1" itemprop="foo"></div><div itemscope itemref="id1"><div itemprop="bar"></div></div>'); + var testEl = parEl.childNodes[1]; + var DSL = testEl.properties.names; + assert_equals( DSL.length, 2, 'length (before test)' ); + assert_equals( DSL[0], 'foo', 'item 0 (before test)' ); + assert_equals( DSL[1], 'bar', 'item 1 (before test)' ); + document.body.appendChild(testEl); + var step1length = DSL.length; + var step1prop0 = DSL[0]; + var step1prop1 = DSL[1]; + parEl.appendChild(testEl); + assert_equals( step1length, 1, 'length after changing parent' ); + assert_equals( step1prop0, 'bar', 'item 0 after changing parent' ); + assert_true( !step1prop1, 'item 1 after changing parent' ); + assert_equals( DSL.length, 2, 'length after re-parenting' ); + assert_equals( DSL[0], 'foo', 'item 0 after re-parenting' ); + assert_equals( DSL[1], 'bar', 'item 1 after re-parenting' ); + document.body.appendChild(parEl); + var step2length = DSL.length; + var step2prop0 = DSL[0]; + var step2prop1 = DSL[1]; + document.createElement('div').appendChild(testEl); + var step3length = DSL.length; + var step3prop0 = DSL[0]; + var step3prop1 = DSL[1]; + parEl.appendChild(testEl); + var step4length = DSL.length; + var step4prop0 = DSL[0]; + var step4prop1 = DSL[1]; + document.body.removeChild(parEl); + assert_equals( step2length, 2, 'length (before test) when appended to document' ); + assert_equals( step2prop0, 'foo', 'item 0 (before test) when appended to document' ); + assert_equals( step2prop1, 'bar', 'item 1 (before test) when appended to document' ); + assert_equals( step3length, 1, 'length after changing parent when appended to document' ); + assert_equals( step3prop0, 'bar', 'item 0 after changing parent when appended to document' ); + assert_true( !step3prop1, 'item 1 after changing parent when appended to document' ); + assert_equals( step4length, 2, 'length after re-parenting when appended to document' ); + assert_equals( step4prop0, 'foo', 'item 0 after re-parenting when appended to document' ); + assert_equals( step4prop1, 'bar', 'item 1 after re-parenting when appended to document' ); +}, 'names must update when appending elements with itemref to different parents'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div><div itemprop="foo"></div></div>'); + var DSL = testEl.properties.names; + assert_equals( DSL.length, 1, 'length (before test)' ); + assert_equals( DSL[0], 'foo', 'item 0 (before test)' ); + testEl.firstChild.itemScope = true; + assert_equals( DSL.length, 0, 'length after setting itemscope' ); + assert_true( !DSL[0], 'item 0 after setting itemscope' ); + testEl.firstChild.removeAttribute('itemscope'); + assert_equals( DSL.length, 1, 'length after removing itemscope attribute' ); + assert_equals( DSL[0], 'foo', 'item 0 after removing itemscope attribute' ); +}, 'names must update when changing itemscope of children'); + +/* potential bugs */ +test(function () { + var parEl = makeEl('div',{},'<div id="id1"></div>'); + var testEl = makeEl('div',{itemscope:'itemscope',itemref:'id1 id2'}); + parEl.appendChild(testEl); + testEl.appendChild(makeEl('meta',{itemprop:'foo',content:'test'})); + testEl.appendChild(makeEl('audio',{itemprop:'foo',src:'http://example.org/'},'contained text')); + testEl.appendChild(makeEl('embed',{itemprop:'foo',src:'http://example.org/'})); + testEl.appendChild(makeEl('iframe',{itemprop:'foo',src:'http://example.org/'},'contained text')); + testEl.appendChild(makeEl('img',{itemprop:'foo',src:'http://example.org/'})); + testEl.appendChild(makeEl('source',{itemprop:'foo',src:'http://example.org/'})); + testEl.appendChild(makeEl('track',{itemprop:'foo',src:'http://example.org/'})); + testEl.appendChild(makeEl('video',{itemprop:'foo',src:'http://example.org/'},'contained text')); + testEl.appendChild(makeEl('a',{itemprop:'foo',href:'http://example.org/'},'contained text')); + testEl.appendChild(makeEl('area',{itemprop:'foo',href:'http://example.org/'})); + testEl.appendChild(makeEl('link',{itemprop:'foo',href:'http://example.org/'})); + testEl.appendChild(makeEl('object',{itemprop:'foo',data:'http://example.org/'},'contained text')); + parEl.appendChild(makeEl('time',{itemprop:'bar',id:'id2'},'te <span itemprop="foo" itemscope>st</span> ing')); + testEl.appendChild(makeEl('time',{itemprop:'foo',datetime:'test'},'te <span itemprop="foo" itemscope>st</span> ing')); + parEl.firstChild.appendChild(makeEl('div',{itemprop:'baz'},'te <span itemprop="foo" itemscope>st</span> ing')); + testEl.appendChild(makeEl('madeuponthespot',{itemprop:'baz'},'te <span itemprop="foo" itemscope>st</span> ing')); + var properties, PNLfoo, PNLbar, PNLbaz, fooValues, barValues, bazValues, allArrays, snapshot = []; + document.body.appendChild(parEl); + try { + properties = testEl.properties; + PNLfoo = properties.namedItem('foo'); + PNLbar = properties.namedItem('bar'); + PNLbaz = properties.namedItem('baz'); + fooValues = PNLfoo.getValues(); + barValues = PNLbar.getValues(); + bazValues = PNLbaz.getValues(); + allArrays = [properties,PNLfoo,PNLbar,PNLbaz,fooValues,barValues,bazValues]; + for( var a = 0; a < allArrays.length; a++ ) { + snapshot[a] = []; + for( var b = 0; b < allArrays[a].length; b++ ) { + snapshot[a][b] = allArrays[a][b]; + } + } + } catch(e) { /* need to clean up */ } + document.body.removeChild(parEl); + var c, d; + for( c = 0; c < allArrays.length; c++ ) { + for( d = 0; d < allArrays[c].length; d++ ) { + assert_equals( snapshot[c][d], allArrays[c][d], 'allArrays['+c+']['+d+']' ); + } + } + var newArrays = [testEl.properties,testEl.properties.namedItem('foo'),testEl.properties.namedItem('bar'),testEl.properties.namedItem('baz'),testEl.properties.namedItem('foo').getValues(),testEl.properties.namedItem('bar').getValues(),testEl.properties.namedItem('baz').getValues()]; + for( c = 0; c < newArrays.length; c++ ) { + for( d = 0; d < newArrays[c].length; d++ ) { + assert_equals( snapshot[c][d], newArrays[c][d], 'newArrays['+c+']['+d+']' ); + } + } +}, 'collections must survive the parent\'s removal from the document'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'}); + testEl.appendChild(makeEl('meta',{itemprop:'foo',content:'test'})); + testEl.appendChild(makeEl('audio',{itemprop:'foo',src:'http://example.org/'},'contained text')); + testEl.appendChild(makeEl('embed',{itemprop:'foo',src:'http://example.org/'})); + testEl.appendChild(makeEl('iframe',{itemprop:'foo',src:'http://example.org/'},'contained text')); + testEl.appendChild(makeEl('img',{itemprop:'foo',src:'http://example.org/'})); + testEl.appendChild(makeEl('source',{itemprop:'foo',src:'http://example.org/'})); + testEl.appendChild(makeEl('track',{itemprop:'foo',src:'http://example.org/'})); + testEl.appendChild(makeEl('video',{itemprop:'foo',src:'http://example.org/'},'contained text')); + testEl.appendChild(makeEl('a',{itemprop:'foo',href:'http://example.org/'},'contained text')); + testEl.appendChild(makeEl('area',{itemprop:'foo',href:'http://example.org/'})); + testEl.appendChild(makeEl('link',{itemprop:'foo',href:'http://example.org/'})); + testEl.appendChild(makeEl('object',{itemprop:'foo',data:'http://example.org/'},'contained text')); + testEl.appendChild(makeEl('time',{itemprop:'bar',id:'id2'},'te <span itemprop="foo" itemscope>st</span> ing')); + testEl.appendChild(makeEl('time',{itemprop:'foo',datetime:'test'},'te <span itemprop="foo" itemscope>st</span> ing')); + testEl.appendChild(makeEl('div',{itemprop:'baz'},'te <span itemprop="foo" itemscope>st</span> ing')); + testEl.appendChild(makeEl('madeuponthespot',{itemprop:'baz'},'te <span itemprop="foo" itemscope>st</span> ing')); + var properties, PNLfoo, PNLbar, PNLbaz, fooValues, barValues, bazValues, allArrays, snapshot = []; + document.body.appendChild(testEl); + try { + properties = testEl.properties; + PNLfoo = properties.namedItem('foo'); + PNLbar = properties.namedItem('bar'); + PNLbaz = properties.namedItem('baz'); + fooValues = PNLfoo.getValues(); + barValues = PNLbar.getValues(); + bazValues = PNLbaz.getValues(); + allArrays = [properties,PNLfoo,PNLbar,PNLbaz,fooValues,barValues,bazValues]; + for( var a = 0; a < allArrays.length; a++ ) { + snapshot[a] = []; + for( var b = 0; b < allArrays[a].length; b++ ) { + snapshot[a][b] = allArrays[a][b]; + } + } + } catch(e) { /* need to clean up */ } + document.body.removeChild(testEl); + var c, d; + for( c = 0; c < allArrays.length; c++ ) { + for( d = 0; d < allArrays[c].length; d++ ) { + assert_equals( snapshot[c][d], allArrays[c][d], 'allArrays['+c+']['+d+']' ); + } + } + var newArrays = [testEl.properties,testEl.properties.namedItem('foo'),testEl.properties.namedItem('bar'),testEl.properties.namedItem('baz'),testEl.properties.namedItem('foo').getValues(),testEl.properties.namedItem('bar').getValues(),testEl.properties.namedItem('baz').getValues()]; + for( c = 0; c < newArrays.length; c++ ) { + for( d = 0; d < newArrays[c].length; d++ ) { + assert_equals( snapshot[c][d], newArrays[c][d], 'newArrays['+c+']['+d+']' ); + } + } +}, 'collections must survive the item\'s removal from the document'); + +/* override_builtins */ +test(function () { + //http://dev.w3.org/2006/webapi/WebIDL/#named-properties + //[OverrideBuiltins] is not declared for any of the properties, hence no overriding is allowed + var testEl = makeEl('div',{itemscope:'itemscope'}); + var namedItem = testEl.properties.namedItem; + var item = testEl.properties.item; + var names = testEl.properties.names; + testEl.innerHTML = '<div itemprop="namedItem length item names"></div>'; + assert_equals( testEl.properties['namedItem'], namedItem, 'namedItem' ); + assert_equals( testEl.properties['length'], 1, 'length' ); + assert_equals( testEl.properties['item'], item, 'item' ); + assert_equals( testEl.properties['names'], names, 'names' ); +}, 'itemprop names must not override builtin properties'); + +/* casting */ +//when calling object[other_object], ECMAScript treats other_object as a named property so it casts it to a string using toString +//when looking up a named property, ECMAScript and WebIDL <http://dev.w3.org/2006/webapi/WebIDL/#named-properties> will prefer an array index property name +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="2"></div><div itemprop="0"></div>'); + assert_equals( testEl.properties.item('0'), testEl.properties.item(0), '0' ); + assert_equals( testEl.properties.item('2'), testEl.properties.item(2), '2' ); +}, 'properties.item(integerString) should cast to a number'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="2"></div><div itemprop="0"></div>'); + assert_equals( testEl.properties['0'], testEl.properties.item(0), '0' ); + assert_equals( testEl.properties['2'], window.undefined, '2' ); +}, 'properties[integerString] should act as a numeric index'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="0"></div>'); + assert_equals( testEl.properties.namedItem(0), testEl.properties.namedItem('0'), '0' ); + assert_true( testEl.properties.namedItem(0) instanceof PropertyNodeList , 'instanceof' ); +}, 'properties.namedItem(integer) should cast to a string'); +test(function () { + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="2 foo"></div><div itemprop="0"></div>'); + assert_equals( testEl.properties[{ toString: function(){return 'foo';}, valueOf: function(){return 1;} }][0], testEl.firstChild, 'foo' ); + assert_equals( testEl.properties[{ toString: function(){return '0';}, valueOf: function(){return 1;} }], testEl.firstChild, '0' ); + assert_equals( testEl.properties[{ toString: function(){return '2';}, valueOf: function(){return 0;} }], window.undefined, '2' ); +}, 'properties[someObject] should cast toString before using whichever casting applies'); + +/* loops and evil itemref */ +test(function () { + //This should have 1 property on each itemscope, pointing only to its direct child + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemscope itemprop="foo"><div itemprop="bar"></div></div>'); + assert_equals( testEl.properties.length, 1, 'outer length' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'outer properties[0]' ); + assert_true( !testEl.properties[1], 'outer properties[1]' ); + assert_equals( testEl.properties.namedItem('foo').length, 1, 'outer foo.length' ); + assert_equals( testEl.properties.namedItem('foo')[0], testEl.firstChild, 'outer foo[0]' ); + assert_true( !testEl.properties.namedItem('foo')[1], 'outer foo[1]' ); + assert_equals( testEl.properties.namedItem('bar').length, 0, 'outer bar.length' ); + assert_true( !testEl.properties.namedItem('bar')[0], 'outer bar[0]' ); + assert_equals( testEl.properties.names.length, 1, 'outer names.length' ); + assert_equals( testEl.properties.names[0], 'foo', 'outer names[0]' ); + assert_true( !testEl.properties.names[1], 'outer names[1]' ); + assert_equals( testEl.firstChild.properties.length, 1, 'inner length' ); + assert_equals( testEl.firstChild.properties[0], testEl.firstChild.firstChild, 'inner properties[0]' ); + assert_true( !testEl.firstChild.properties[1], 'inner properties[1]' ); + assert_equals( testEl.firstChild.properties.namedItem('foo').length, 0, 'inner foo.length' ); + assert_true( !testEl.firstChild.properties.namedItem('foo')[0], 'inner foo[0]' ); + assert_equals( testEl.firstChild.properties.namedItem('bar').length, 1, 'inner bar.length' ); + assert_equals( testEl.firstChild.properties.namedItem('bar')[0], testEl.firstChild.firstChild, 'inner bar[0]' ); + assert_true( !testEl.firstChild.properties.namedItem('foo')[1], 'inner foo[1]' ); + assert_equals( testEl.firstChild.properties.names.length, 1, 'inner names.length' ); + assert_equals( testEl.firstChild.properties.names[0], 'bar', 'inner names[0]' ); + assert_true( !testEl.firstChild.properties.names[1], 'inner names[1]' ); +}, 'simple nested itemscope'); +test(function () { + //This should have 1 property on each itemscope, pointing only to its direct child + var testEl = makeEl('div',{itemscope:'itemscope',itemref:'id1'},'<div itemscope itemprop="foo" id="id1" itemref="id2"><div itemprop="bar" id="id2"></div></div>'); + assert_equals( testEl.properties.length, 1, 'outer length' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'outer properties[0]' ); + assert_true( !testEl.properties[1], 'outer properties[1]' ); + assert_equals( testEl.properties.namedItem('foo').length, 1, 'outer foo.length' ); + assert_equals( testEl.properties.namedItem('foo')[0], testEl.firstChild, 'outer foo[0]' ); + assert_true( !testEl.properties.namedItem('foo')[1], 'outer foo[1]' ); + assert_equals( testEl.properties.namedItem('bar').length, 0, 'outer bar.length' ); + assert_true( !testEl.properties.namedItem('bar')[0], 'outer bar[0]' ); + assert_equals( testEl.properties.names.length, 1, 'outer names.length' ); + assert_equals( testEl.properties.names[0], 'foo', 'outer names[0]' ); + assert_true( !testEl.properties.names[1], 'outer names[1]' ); + assert_equals( testEl.firstChild.properties.length, 1, 'inner length' ); + assert_equals( testEl.firstChild.properties[0], testEl.firstChild.firstChild, 'inner properties[0]' ); + assert_true( !testEl.firstChild.properties[1], 'inner properties[1]' ); + assert_equals( testEl.firstChild.properties.namedItem('foo').length, 0, 'inner foo.length' ); + assert_true( !testEl.firstChild.properties.namedItem('foo')[0], 'inner foo[0]' ); + assert_equals( testEl.firstChild.properties.namedItem('bar').length, 1, 'inner bar.length' ); + assert_equals( testEl.firstChild.properties.namedItem('bar')[0], testEl.firstChild.firstChild, 'inner bar[0]' ); + assert_true( !testEl.firstChild.properties.namedItem('foo')[1], 'inner foo[1]' ); + assert_equals( testEl.firstChild.properties.names.length, 1, 'inner names.length' ); + assert_equals( testEl.firstChild.properties.names[0], 'bar', 'inner names[0]' ); + assert_true( !testEl.firstChild.properties.names[1], 'inner names[1]' ); + document.body.appendChild(testEl); + try { + assert_equals( testEl.properties.length, 1, 'outer length when appended to document' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'outer properties[0] when appended to document' ); + assert_true( !testEl.properties[1], 'outer properties[1] when appended to document' ); + assert_equals( testEl.properties.namedItem('foo').length, 1, 'outer foo.length when appended to document' ); + assert_equals( testEl.properties.namedItem('foo')[0], testEl.firstChild, 'outer foo[0] when appended to document' ); + assert_true( !testEl.properties.namedItem('foo')[1], 'outer foo[1] when appended to document' ); + assert_equals( testEl.properties.namedItem('bar').length, 0, 'outer bar.length when appended to document' ); + assert_true( !testEl.properties.namedItem('bar')[0], 'outer bar[0] when appended to document' ); + assert_equals( testEl.properties.names.length, 1, 'outer names.length when appended to document' ); + assert_equals( testEl.properties.names[0], 'foo', 'outer names[0] when appended to document' ); + assert_true( !testEl.properties.names[1], 'outer names[1] when appended to document' ); + assert_equals( testEl.firstChild.properties.length, 1, 'inner length when appended to document' ); + assert_equals( testEl.firstChild.properties[0], testEl.firstChild.firstChild, 'inner properties[0] when appended to document' ); + assert_true( !testEl.firstChild.properties[1], 'inner properties[1] when appended to document' ); + assert_equals( testEl.firstChild.properties.namedItem('foo').length, 0, 'inner foo.length when appended to document' ); + assert_true( !testEl.firstChild.properties.namedItem('foo')[0], 'inner foo[0] when appended to document' ); + assert_equals( testEl.firstChild.properties.namedItem('bar').length, 1, 'inner bar.length when appended to document' ); + assert_equals( testEl.firstChild.properties.namedItem('bar')[0], testEl.firstChild.firstChild, 'inner bar[0] when appended to document' ); + assert_true( !testEl.firstChild.properties.namedItem('foo')[1], 'inner foo[1] when appended to document' ); + assert_equals( testEl.firstChild.properties.names.length, 1, 'inner names.length when appended to document' ); + assert_equals( testEl.firstChild.properties.names[0], 'bar', 'inner names[0] when appended to document' ); + assert_true( !testEl.firstChild.properties.names[1], 'inner names[1] when appended to document' ); + } catch(e) { + document.body.removeChild(testEl); + throw (e); + } + document.body.removeChild(testEl); +}, 'simple nested itemscope with itemref'); +test(function () { + //This should have 3 properties on the item; foo, bar and baz + var parEl = makeEl('div',{},'<div itemprop="foo" id="id2"></div><div itemscope itemref="id1 id2"><div itemprop="bar"></div></div><div itemprop="baz" id="id1"></div>'); + var testEl = parEl.childNodes[1]; + assert_equals( testEl.properties.length, 3, 'length' ); + assert_equals( testEl.properties[0], parEl.firstChild, 'properties[0]' ); + assert_equals( testEl.properties[1], testEl.firstChild, 'properties[1]' ); + assert_equals( testEl.properties[2], parEl.lastChild, 'properties[2]' ); + assert_true( !testEl.properties[3], 'properties[3]' ); + assert_equals( testEl.properties.namedItem('foo').length, 1, 'foo.length' ); + assert_equals( testEl.properties.namedItem('foo')[0], parEl.firstChild, 'foo[0]' ); + assert_true( !testEl.properties.namedItem('foo')[1], 'foo[1]' ); + assert_equals( testEl.properties.namedItem('bar').length, 1, 'bar.length' ); + assert_equals( testEl.properties.namedItem('bar')[0], testEl.firstChild, 'bar[0]' ); + assert_true( !testEl.properties.namedItem('bar')[1], 'bar[1]' ); + assert_equals( testEl.properties.namedItem('baz').length, 1, 'baz.length' ); + assert_equals( testEl.properties.namedItem('baz')[0], parEl.lastChild, 'baz[0]' ); + assert_true( !testEl.properties.namedItem('baz')[1], 'baz[1]' ); + assert_equals( testEl.properties.names.length, 3, 'names.length' ); + assert_equals( testEl.properties.names[0], 'foo', 'names[0]' ); + assert_equals( testEl.properties.names[1], 'bar', 'names[1]' ); + assert_equals( testEl.properties.names[2], 'baz', 'names[2]' ); + assert_true( !testEl.properties.names[3], 'names[3]' ); + document.body.appendChild(parEl); + try { + assert_equals( testEl.properties.length, 3, 'length when appended to document' ); + assert_equals( testEl.properties[0], parEl.firstChild, 'properties[0] when appended to document' ); + assert_equals( testEl.properties[1], testEl.firstChild, 'properties[1] when appended to document' ); + assert_equals( testEl.properties[2], parEl.lastChild, 'properties[2] when appended to document' ); + assert_true( !testEl.properties[3], 'properties[3] when appended to document' ); + assert_equals( testEl.properties.namedItem('foo').length, 1, 'foo.length when appended to document' ); + assert_equals( testEl.properties.namedItem('foo')[0], parEl.firstChild, 'foo[0] when appended to document' ); + assert_true( !testEl.properties.namedItem('foo')[1], 'foo[1] when appended to document' ); + assert_equals( testEl.properties.namedItem('bar').length, 1, 'bar.length when appended to document' ); + assert_equals( testEl.properties.namedItem('bar')[0], testEl.firstChild, 'bar[0] when appended to document' ); + assert_true( !testEl.properties.namedItem('bar')[1], 'bar[1] when appended to document' ); + assert_equals( testEl.properties.namedItem('baz').length, 1, 'baz.length when appended to document' ); + assert_equals( testEl.properties.namedItem('baz')[0], parEl.lastChild, 'baz[0] when appended to document' ); + assert_true( !testEl.properties.namedItem('baz')[1], 'baz[1] when appended to document' ); + assert_equals( testEl.properties.names.length, 3, 'names.length when appended to document' ); + assert_equals( testEl.properties.names[0], 'foo', 'names[0] when appended to document' ); + assert_equals( testEl.properties.names[1], 'bar', 'names[1] when appended to document' ); + assert_equals( testEl.properties.names[2], 'baz', 'names[2] when appended to document' ); + assert_true( !testEl.properties.names[3], 'names[3] when appended to document' ); + } catch(e) { + document.body.removeChild(parEl); + throw (e); + } + document.body.removeChild(parEl); +}, 'simple sibling itemref'); +test(function () { + //This should have no properties + var testEl = makeEl('div',{itemscope:'itemscope',id:'id1',itemref:'id1',itemprop:'foo'}); + assert_equals( testEl.properties.length, 0, 'length' ); + assert_true( !testEl.properties[0], 'properties[0]' ); + assert_equals( testEl.properties.namedItem('foo').length, 0, 'foo.length' ); + assert_true( !testEl.properties.namedItem('foo')[0], 'foo[0]' ); + assert_equals( testEl.properties.names.length, 0, 'names.length' ); + assert_true( !testEl.properties.names[0], 'names[0]' ); + document.body.appendChild(testEl); + try { + assert_equals( testEl.properties.length, 0, 'length when appended to document' ); + assert_true( !testEl.properties[0], 'properties[0] when appended to document' ); + assert_equals( testEl.properties.namedItem('foo').length, 0, 'foo.length when appended to document' ); + assert_true( !testEl.properties.namedItem('foo')[0], 'foo[0] when appended to document' ); + assert_equals( testEl.properties.names.length, 0, 'names.length when appended to document' ); + assert_true( !testEl.properties.names[0], 'names[0] when appended to document' ); + } catch(e) { + document.body.removeChild(testEl); + throw (e); + } + document.body.removeChild(testEl); +}, 'itemref pointing to itself'); +test(function () { + //This should have 1 property, pointing to the child + var testEl = makeEl('div',{itemscope:'itemscope',id:'id1',itemref:'id1',itemprop:'foo'},'<div itemprop="bar"></div>'); + assert_equals( testEl.properties.length, 1, 'length' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'properties[0]' ); + assert_true( !testEl.properties[1], 'properties[1]' ); + assert_equals( testEl.properties.namedItem('foo').length, 0, 'foo.length' ); + assert_true( !testEl.properties.namedItem('foo')[0], 'foo[0]' ); + assert_equals( testEl.properties.namedItem('bar').length, 1, 'bar.length' ); + assert_equals( testEl.properties.namedItem('bar')[0], testEl.firstChild, 'bar[0]' ); + assert_true( !testEl.properties.namedItem('bar')[1], 'bar[1]' ); + assert_equals( testEl.properties.names.length, 1, 'names.length' ); + assert_equals( testEl.properties.names[0], 'bar', 'names[0]' ); + assert_true( !testEl.properties.names[1], 'names[1]' ); + document.body.appendChild(testEl); + try { + assert_equals( testEl.properties.length, 1, 'length when appended to document' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'properties[0] when appended to document' ); + assert_true( !testEl.properties[1], 'properties[1] when appended to document' ); + assert_equals( testEl.properties.namedItem('foo').length, 0, 'foo.length when appended to document' ); + assert_true( !testEl.properties.namedItem('foo')[0], 'foo[0] when appended to document' ); + assert_equals( testEl.properties.namedItem('bar').length, 1, 'bar.length when appended to document' ); + assert_equals( testEl.properties.namedItem('bar')[0], testEl.firstChild, 'bar[0] when appended to document' ); + assert_true( !testEl.properties.namedItem('bar')[1], 'bar[1] when appended to document' ); + assert_equals( testEl.properties.names.length, 1, 'names.length when appended to document' ); + assert_equals( testEl.properties.names[0], 'bar', 'names[0] when appended to document' ); + assert_true( !testEl.properties.names[1], 'names[1] when appended to document' ); + } catch(e) { + document.body.removeChild(testEl); + throw (e); + } + document.body.removeChild(testEl); +}, 'itemref pointing to itself with child'); +test(function () { + //This should have 1 property on each itemscope, pointing only to its direct child + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemscope itemprop="foo" id="id1" itemref="id1"><div itemprop="bar"></div></div>'); + assert_equals( testEl.properties.length, 1, 'outer length' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'outer properties[0]' ); + assert_true( !testEl.properties[1], 'outer properties[1]' ); + assert_equals( testEl.properties.namedItem('foo').length, 1, 'outer foo.length' ); + assert_equals( testEl.properties.namedItem('foo')[0], testEl.firstChild, 'outer foo[0]' ); + assert_true( !testEl.properties.namedItem('foo')[1], 'outer foo[1]' ); + assert_equals( testEl.properties.namedItem('bar').length, 0, 'outer bar.length' ); + assert_true( !testEl.properties.namedItem('bar')[0], 'outer bar[0]' ); + assert_equals( testEl.properties.names.length, 1, 'outer names.length' ); + assert_equals( testEl.properties.names[0], 'foo', 'outer names[0]' ); + assert_true( !testEl.properties.names[1], 'outer names[1]' ); + assert_equals( testEl.firstChild.properties.length, 1, 'inner length' ); + assert_equals( testEl.firstChild.properties[0], testEl.firstChild.firstChild, 'inner properties[0]' ); + assert_true( !testEl.firstChild.properties[1], 'inner properties[1]' ); + assert_equals( testEl.firstChild.properties.namedItem('foo').length, 0, 'inner foo.length' ); + assert_true( !testEl.firstChild.properties.namedItem('foo')[0], 'inner foo[0]' ); + assert_equals( testEl.firstChild.properties.namedItem('bar').length, 1, 'inner bar.length' ); + assert_equals( testEl.firstChild.properties.namedItem('bar')[0], testEl.firstChild.firstChild, 'inner bar[0]' ); + assert_true( !testEl.firstChild.properties.namedItem('bar')[1], 'inner bar[1]' ); + assert_equals( testEl.firstChild.properties.names.length, 1, 'inner names.length' ); + assert_equals( testEl.firstChild.properties.names[0], 'bar', 'inner names[0]' ); + assert_true( !testEl.firstChild.properties.names[1], 'inner names[1]' ); + document.body.appendChild(testEl); + try { + assert_equals( testEl.properties.length, 1, 'outer length when appended to document' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'outer properties[0] when appended to document' ); + assert_true( !testEl.properties[1], 'outer properties[1] when appended to document' ); + assert_equals( testEl.properties.namedItem('foo').length, 1, 'outer foo.length when appended to document' ); + assert_equals( testEl.properties.namedItem('foo')[0], testEl.firstChild, 'outer foo[0] when appended to document' ); + assert_true( !testEl.properties.namedItem('foo')[1], 'outer foo[1] when appended to document' ); + assert_equals( testEl.properties.namedItem('bar').length, 0, 'outer bar.length when appended to document' ); + assert_true( !testEl.properties.namedItem('bar')[0], 'outer bar[0] when appended to document' ); + assert_equals( testEl.properties.names.length, 1, 'outer names.length when appended to document' ); + assert_equals( testEl.properties.names[0], 'foo', 'outer names[0] when appended to document' ); + assert_true( !testEl.properties.names[1], 'outer names[1] when appended to document' ); + assert_equals( testEl.firstChild.properties.length, 1, 'inner length when appended to document' ); + assert_equals( testEl.firstChild.properties[0], testEl.firstChild.firstChild, 'inner properties[0] when appended to document' ); + assert_true( !testEl.firstChild.properties[1], 'inner properties[1] when appended to document' ); + assert_equals( testEl.firstChild.properties.namedItem('foo').length, 0, 'inner foo.length when appended to document' ); + assert_true( !testEl.firstChild.properties.namedItem('foo')[0], 'inner foo[0] when appended to document' ); + assert_equals( testEl.firstChild.properties.namedItem('bar').length, 1, 'inner bar.length when appended to document' ); + assert_equals( testEl.firstChild.properties.namedItem('bar')[0], testEl.firstChild.firstChild, 'inner bar[0] when appended to document' ); + assert_true( !testEl.firstChild.properties.namedItem('bar')[1], 'inner bar[1] when appended to document' ); + assert_equals( testEl.firstChild.properties.names.length, 1, 'inner names.length when appended to document' ); + assert_equals( testEl.firstChild.properties.names[0], 'bar', 'inner names[0] when appended to document' ); + assert_true( !testEl.firstChild.properties.names[1], 'inner names[1] when appended to document' ); + } catch(e) { + document.body.removeChild(testEl); + throw (e); + } + document.body.removeChild(testEl); +}, 'nested itemref pointing to itself with child'); +test(function () { + //Each itemscope has one property, pointing to the other one + var testEl = makeEl('div',{},'<div id="id1" itemprop="foo" itemscope itemref="id2"></div><div id="id2" itemprop="bar" itemscope itemref="id1"></div>'); + assert_equals( testEl.firstChild.properties.length, 1, 'id1 length' ); + assert_equals( testEl.firstChild.properties[0], testEl.lastChild, 'id1 properties[0]' ); + assert_true( !testEl.firstChild.properties[1], 'id1 properties[1]' ); + assert_equals( testEl.firstChild.properties.namedItem('foo').length, 0, 'id1 foo.length' ); + assert_true( !testEl.firstChild.properties.namedItem('foo')[0], 'id1 foo[0]' ); + assert_equals( testEl.firstChild.properties.namedItem('bar').length, 1, 'id1 bar.length' ); + assert_equals( testEl.firstChild.properties.namedItem('bar')[0], testEl.lastChild, 'id1 bar[0]' ); + assert_true( !testEl.firstChild.properties.namedItem('bar')[1], 'id1 bar[1]' ); + assert_equals( testEl.firstChild.properties.names.length, 1, 'id1 names.length' ); + assert_equals( testEl.firstChild.properties.names[0], 'bar', 'id1 names[0]' ); + assert_true( !testEl.firstChild.properties.names[1], 'id1 names[1]' ); + assert_equals( testEl.lastChild.properties.length, 1, 'id2 length' ); + assert_equals( testEl.lastChild.properties[0], testEl.firstChild, 'id2 properties[0]' ); + assert_true( !testEl.lastChild.properties[1], 'id2 properties[1]' ); + assert_equals( testEl.lastChild.properties.namedItem('foo').length, 1, 'id2 foo.length' ); + assert_equals( testEl.lastChild.properties.namedItem('foo')[0], testEl.firstChild, 'id2 foo[0]' ); + assert_true( !testEl.lastChild.properties.namedItem('foo')[1], 'id2 foo[1]' ); + assert_equals( testEl.lastChild.properties.namedItem('bar').length, 0, 'id2 bar.length' ); + assert_true( !testEl.lastChild.properties.namedItem('bar')[0], 'id2 bar[0]' ); + assert_equals( testEl.lastChild.properties.names.length, 1, 'id2 names.length' ); + assert_equals( testEl.lastChild.properties.names[0], 'foo', 'id2 names[0]' ); + assert_true( !testEl.lastChild.properties.names[1], 'id2 names[1]' ); + document.body.appendChild(testEl); + try { + assert_equals( testEl.firstChild.properties.length, 1, 'id1 length when appended to document' ); + assert_equals( testEl.firstChild.properties[0], testEl.lastChild, 'id1 properties[0] when appended to document' ); + assert_true( !testEl.firstChild.properties[1], 'id1 properties[1] when appended to document' ); + assert_equals( testEl.firstChild.properties.namedItem('foo').length, 0, 'id1 foo.length when appended to document' ); + assert_true( !testEl.firstChild.properties.namedItem('foo')[0], 'id1 foo[0] when appended to document' ); + assert_equals( testEl.firstChild.properties.namedItem('bar').length, 1, 'id1 bar.length when appended to document' ); + assert_equals( testEl.firstChild.properties.namedItem('bar')[0], testEl.lastChild, 'id1 bar[0] when appended to document' ); + assert_true( !testEl.firstChild.properties.namedItem('bar')[1], 'id1 bar[1] when appended to document' ); + assert_equals( testEl.firstChild.properties.names.length, 1, 'id1 names.length when appended to document' ); + assert_equals( testEl.firstChild.properties.names[0], 'bar', 'id1 names[0] when appended to document' ); + assert_true( !testEl.firstChild.properties.names[1], 'id1 names[1] when appended to document' ); + assert_equals( testEl.lastChild.properties.length, 1, 'id2 length when appended to document' ); + assert_equals( testEl.lastChild.properties[0], testEl.firstChild, 'id2 properties[0] when appended to document' ); + assert_true( !testEl.lastChild.properties[1], 'id2 properties[1] when appended to document' ); + assert_equals( testEl.lastChild.properties.namedItem('foo').length, 1, 'id2 foo.length when appended to document' ); + assert_equals( testEl.lastChild.properties.namedItem('foo')[0], testEl.firstChild, 'id2 foo[0] when appended to document' ); + assert_true( !testEl.lastChild.properties.namedItem('foo')[1], 'id2 foo[1] when appended to document' ); + assert_equals( testEl.lastChild.properties.namedItem('bar').length, 0, 'id2 bar.length when appended to document' ); + assert_true( !testEl.lastChild.properties.namedItem('bar')[0], 'id2 bar[0] when appended to document' ); + assert_equals( testEl.lastChild.properties.names.length, 1, 'id2 names.length when appended to document' ); + assert_equals( testEl.lastChild.properties.names[0], 'foo', 'id2 names[0] when appended to document' ); + assert_true( !testEl.lastChild.properties.names[1], 'id2 names[1] when appended to document' ); + } catch(e) { + document.body.removeChild(testEl); + throw (e); + } + document.body.removeChild(testEl); +}, 'mutually referencing siblings'); +test(function () { + //Root has 2 properties, foo and bar + //Each itemscope has one property, pointing to the other one + var testEl = makeEl('div',{itemscope:'itemscope'},'<div id="id1" itemprop="foo" itemscope itemref="id2"></div><div id="id2" itemprop="bar" itemscope itemref="id1"></div>'); + assert_equals( testEl.properties.length, 2, 'root length' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'root properties[0]' ); + assert_equals( testEl.properties[1], testEl.lastChild, 'root properties[1]' ); + assert_true( !testEl.properties[2], 'root properties[2]' ); + assert_equals( testEl.properties.namedItem('foo').length, 1, 'root foo.length' ); + assert_equals( testEl.properties.namedItem('foo')[0], testEl.firstChild, 'root foo[0]' ); + assert_true( !testEl.properties.namedItem('foo')[1], 'root foo[1]' ); + assert_equals( testEl.properties.namedItem('bar').length, 1, 'root bar.length' ); + assert_equals( testEl.properties.namedItem('bar')[0], testEl.lastChild, 'root bar[0]' ); + assert_true( !testEl.properties.namedItem('bar')[1], 'root bar[1]' ); + assert_equals( testEl.properties.names.length, 2, 'root names.length' ); + assert_equals( testEl.properties.names[0], 'foo', 'root names[0]' ); + assert_equals( testEl.properties.names[1], 'bar', 'root names[1]' ); + assert_true( !testEl.properties.names[2], 'root names[2]' ); + assert_equals( testEl.firstChild.properties.length, 1, 'id1 length' ); + assert_equals( testEl.firstChild.properties[0], testEl.lastChild, 'id1 properties[0]' ); + assert_true( !testEl.firstChild.properties[1], 'id1 properties[1]' ); + assert_equals( testEl.firstChild.properties.namedItem('foo').length, 0, 'id1 foo.length' ); + assert_true( !testEl.firstChild.properties.namedItem('foo')[0], 'id1 foo[0]' ); + assert_equals( testEl.firstChild.properties.namedItem('bar').length, 1, 'id1 bar.length' ); + assert_equals( testEl.firstChild.properties.namedItem('bar')[0], testEl.lastChild, 'id1 bar[0]' ); + assert_true( !testEl.firstChild.properties.namedItem('bar')[1], 'id1 bar[1]' ); + assert_equals( testEl.firstChild.properties.names.length, 1, 'id1 names.length' ); + assert_equals( testEl.firstChild.properties.names[0], 'bar', 'id1 names[0]' ); + assert_true( !testEl.firstChild.properties.names[1], 'id1 names[1]' ); + assert_equals( testEl.lastChild.properties.length, 1, 'id2 length' ); + assert_equals( testEl.lastChild.properties[0], testEl.firstChild, 'id2 properties[0]' ); + assert_true( !testEl.lastChild.properties[1], 'id2 properties[1]' ); + assert_equals( testEl.lastChild.properties.namedItem('foo').length, 1, 'id2 foo.length' ); + assert_equals( testEl.lastChild.properties.namedItem('foo')[0], testEl.firstChild, 'id2 foo[0]' ); + assert_true( !testEl.lastChild.properties.namedItem('foo')[1], 'id2 foo[1]' ); + assert_equals( testEl.lastChild.properties.namedItem('bar').length, 0, 'id2 bar.length' ); + assert_true( !testEl.lastChild.properties.namedItem('bar')[0], 'id2 bar[0]' ); + assert_equals( testEl.lastChild.properties.names.length, 1, 'id2 names.length' ); + assert_equals( testEl.lastChild.properties.names[0], 'foo', 'id2 names[0]' ); + assert_true( !testEl.lastChild.properties.names[1], 'id2 names[1]' ); + document.body.appendChild(testEl); + try { + assert_equals( testEl.properties.length, 2, 'root length when appended to document' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'root properties[0] when appended to document' ); + assert_equals( testEl.properties[1], testEl.lastChild, 'root properties[1] when appended to document' ); + assert_true( !testEl.properties[2], 'root properties[2] when appended to document' ); + assert_equals( testEl.properties.namedItem('foo').length, 1, 'root foo.length when appended to document' ); + assert_equals( testEl.properties.namedItem('foo')[0], testEl.firstChild, 'root foo[0] when appended to document' ); + assert_true( !testEl.properties.namedItem('foo')[1], 'root foo[1] when appended to document' ); + assert_equals( testEl.properties.namedItem('bar').length, 1, 'root bar.length when appended to document' ); + assert_equals( testEl.properties.namedItem('bar')[0], testEl.lastChild, 'root bar[0] when appended to document' ); + assert_true( !testEl.properties.namedItem('bar')[1], 'root bar[1] when appended to document' ); + assert_equals( testEl.properties.names.length, 2, 'root names.length when appended to document' ); + assert_equals( testEl.properties.names[0], 'foo', 'root names[0] when appended to document' ); + assert_equals( testEl.properties.names[1], 'bar', 'root names[1] when appended to document' ); + assert_true( !testEl.properties.names[2], 'root names[2] when appended to document' ); + assert_equals( testEl.firstChild.properties.length, 1, 'id1 length when appended to document' ); + assert_equals( testEl.firstChild.properties[0], testEl.lastChild, 'id1 properties[0] when appended to document' ); + assert_true( !testEl.firstChild.properties[1], 'id1 properties[1] when appended to document' ); + assert_equals( testEl.firstChild.properties.namedItem('foo').length, 0, 'id1 foo.length when appended to document' ); + assert_true( !testEl.firstChild.properties.namedItem('foo')[0], 'id1 foo[0] when appended to document' ); + assert_equals( testEl.firstChild.properties.namedItem('bar').length, 1, 'id1 bar.length when appended to document' ); + assert_equals( testEl.firstChild.properties.namedItem('bar')[0], testEl.lastChild, 'id1 bar[0] when appended to document' ); + assert_true( !testEl.firstChild.properties.namedItem('bar')[1], 'id1 bar[1] when appended to document' ); + assert_equals( testEl.firstChild.properties.names.length, 1, 'id1 names.length when appended to document' ); + assert_equals( testEl.firstChild.properties.names[0], 'bar', 'id1 names[0] when appended to document' ); + assert_true( !testEl.firstChild.properties.names[1], 'id1 names[1] when appended to document' ); + assert_equals( testEl.lastChild.properties.length, 1, 'id2 length when appended to document' ); + assert_equals( testEl.lastChild.properties[0], testEl.firstChild, 'id2 properties[0] when appended to document' ); + assert_true( !testEl.lastChild.properties[1], 'id2 properties[1] when appended to document' ); + assert_equals( testEl.lastChild.properties.namedItem('foo').length, 1, 'id2 foo.length when appended to document' ); + assert_equals( testEl.lastChild.properties.namedItem('foo')[0], testEl.firstChild, 'id2 foo[0] when appended to document' ); + assert_true( !testEl.lastChild.properties.namedItem('foo')[1], 'id2 foo[1] when appended to document' ); + assert_equals( testEl.lastChild.properties.namedItem('bar').length, 0, 'id2 bar.length when appended to document' ); + assert_true( !testEl.lastChild.properties.namedItem('bar')[0], 'id2 bar[0] when appended to document' ); + assert_equals( testEl.lastChild.properties.names.length, 1, 'id2 names.length when appended to document' ); + assert_equals( testEl.lastChild.properties.names[0], 'foo', 'id2 names[0] when appended to document' ); + assert_true( !testEl.lastChild.properties.names[1], 'id2 names[1] when appended to document' ); + } catch(e) { + document.body.removeChild(testEl); + throw (e); + } + document.body.removeChild(testEl); +}, 'mutually referencing siblings with item parent'); +test(function () { + //Root has two properties, foo and bar + //Bar has two properties, baz and qux + //Qux has one property, bar + var testEl = makeEl('div',{itemscope:'itemscope'},'<div itemprop="foo"></div><div id="id1" itemprop="bar" itemscope><div itemprop="baz"></div><div itemprop="qux" itemscope itemref="id1"></div></div>'); + assert_equals( testEl.properties.length, 2, 'root length' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'root properties[0]' ); + assert_equals( testEl.properties[1], testEl.lastChild, 'root properties[1]' ); + assert_true( !testEl.properties[2], 'root properties[2]' ); + assert_equals( testEl.properties.namedItem('foo').length, 1, 'root foo.length' ); + assert_equals( testEl.properties.namedItem('foo')[0], testEl.firstChild, 'root foo[0]' ); + assert_true( !testEl.properties.namedItem('foo')[1], 'root foo[1]' ); + assert_equals( testEl.properties.namedItem('bar').length, 1, 'root bar.length' ); + assert_equals( testEl.properties.namedItem('bar')[0], testEl.lastChild, 'root bar[0]' ); + assert_true( !testEl.properties.namedItem('bar')[1], 'root bar[1]' ); + assert_equals( testEl.properties.namedItem('baz').length, 0, 'root baz.length' ); + assert_true( !testEl.properties.namedItem('baz')[0], 'root baz[0]' ); + assert_equals( testEl.properties.namedItem('qux').length, 0, 'root qux.length' ); + assert_true( !testEl.properties.namedItem('qux')[0], 'root qux[0]' ); + assert_equals( testEl.properties.names.length, 2, 'root names.length' ); + assert_equals( testEl.properties.names[0], 'foo', 'root names[0]' ); + assert_equals( testEl.properties.names[1], 'bar', 'root names[1]' ); + assert_true( !testEl.properties.names[2], 'root names[2]' ); + assert_equals( testEl.lastChild.properties.length, 2, 'bar length' ); + assert_equals( testEl.lastChild.properties[0], testEl.lastChild.firstChild, 'bar properties[0]' ); + assert_equals( testEl.lastChild.properties[1], testEl.lastChild.lastChild, 'bar properties[1]' ); + assert_true( !testEl.lastChild.properties[2], 'bar properties[2]' ); + assert_equals( testEl.lastChild.properties.namedItem('foo').length, 0, 'bar foo.length' ); + assert_true( !testEl.lastChild.properties.namedItem('foo')[0], 'bar foo[0]' ); + assert_equals( testEl.lastChild.properties.namedItem('bar').length, 0, 'bar bar.length' ); + assert_true( !testEl.lastChild.properties.namedItem('bar')[0], 'bar bar[0]' ); + assert_equals( testEl.lastChild.properties.namedItem('baz').length, 1, 'bar baz.length' ); + assert_equals( testEl.lastChild.properties.namedItem('baz')[0], testEl.lastChild.firstChild, 'bar baz[0]' ); + assert_true( !testEl.lastChild.properties.namedItem('baz')[1], 'bar baz[1]' ); + assert_equals( testEl.lastChild.properties.namedItem('qux').length, 1, 'bar qux.length' ); + assert_equals( testEl.lastChild.properties.namedItem('qux')[0], testEl.lastChild.lastChild, 'bar qux[0]' ); + assert_true( !testEl.lastChild.properties.namedItem('qux')[1], 'bar qux[1]' ); + assert_equals( testEl.lastChild.properties.names.length, 2, 'bar names.length' ); + assert_equals( testEl.lastChild.properties.names[0], 'baz', 'bar names[0]' ); + assert_equals( testEl.lastChild.properties.names[1], 'qux', 'bar names[1]' ); + assert_true( !testEl.lastChild.properties.names[2], 'bar names[2]' ); + assert_equals( testEl.lastChild.lastChild.properties.length, 1, 'qux length' ); + assert_equals( testEl.lastChild.lastChild.properties[0], testEl.lastChild, 'qux properties[0]' ); + assert_true( !testEl.lastChild.lastChild.properties[1], 'qux properties[1]' ); + assert_equals( testEl.lastChild.lastChild.properties.namedItem('foo').length, 0, 'qux foo.length' ); + assert_true( !testEl.lastChild.lastChild.properties.namedItem('foo')[0], 'qux foo[0]' ); + assert_equals( testEl.lastChild.lastChild.properties.namedItem('bar').length, 1, 'qux bar.length' ); + assert_equals( testEl.lastChild.lastChild.properties.namedItem('bar')[0], testEl.lastChild, 'qux bar[0]' ); + assert_true( !testEl.lastChild.lastChild.properties.namedItem('bar')[1], 'qux bar[1]' ); + assert_equals( testEl.lastChild.lastChild.properties.namedItem('baz').length, 0, 'qux baz.length' ); + assert_true( !testEl.lastChild.lastChild.properties.namedItem('baz')[0], 'qux baz[0]' ); + assert_equals( testEl.lastChild.lastChild.properties.namedItem('qux').length, 0, 'qux qux.length' ); + assert_true( !testEl.lastChild.lastChild.properties.namedItem('qux')[0], 'qux qux[0]' ); + assert_equals( testEl.lastChild.lastChild.properties.names.length, 1, 'qux names.length' ); + assert_equals( testEl.lastChild.lastChild.properties.names[0], 'bar', 'qux names[0]' ); + assert_true( !testEl.lastChild.lastChild.properties.names[1], 'qux names[1]' ); + document.body.appendChild(testEl); + try { + assert_equals( testEl.properties.length, 2, 'root length when appended to document' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'root properties[0] when appended to document' ); + assert_equals( testEl.properties[1], testEl.lastChild, 'root properties[1] when appended to document' ); + assert_true( !testEl.properties[2], 'root properties[2] when appended to document' ); + assert_equals( testEl.properties.namedItem('foo').length, 1, 'root foo.length when appended to document' ); + assert_equals( testEl.properties.namedItem('foo')[0], testEl.firstChild, 'root foo[0] when appended to document' ); + assert_true( !testEl.properties.namedItem('foo')[1], 'root foo[1] when appended to document' ); + assert_equals( testEl.properties.namedItem('bar').length, 1, 'root bar.length when appended to document' ); + assert_equals( testEl.properties.namedItem('bar')[0], testEl.lastChild, 'root bar[0] when appended to document' ); + assert_true( !testEl.properties.namedItem('bar')[1], 'root bar[1] when appended to document' ); + assert_equals( testEl.properties.namedItem('baz').length, 0, 'root baz.length when appended to document' ); + assert_true( !testEl.properties.namedItem('baz')[0], 'root baz[0] when appended to document' ); + assert_equals( testEl.properties.namedItem('qux').length, 0, 'root qux.length when appended to document' ); + assert_true( !testEl.properties.namedItem('qux')[0], 'root qux[0] when appended to document' ); + assert_equals( testEl.properties.names.length, 2, 'root names.length when appended to document' ); + assert_equals( testEl.properties.names[0], 'foo', 'root names[0] when appended to document' ); + assert_equals( testEl.properties.names[1], 'bar', 'root names[1] when appended to document' ); + assert_true( !testEl.properties.names[2], 'root names[2] when appended to document' ); + assert_equals( testEl.lastChild.properties.length, 2, 'bar length when appended to document' ); + assert_equals( testEl.lastChild.properties[0], testEl.lastChild.firstChild, 'bar properties[0] when appended to document' ); + assert_equals( testEl.lastChild.properties[1], testEl.lastChild.lastChild, 'bar properties[1] when appended to document' ); + assert_true( !testEl.lastChild.properties[2], 'bar properties[2] when appended to document' ); + assert_equals( testEl.lastChild.properties.namedItem('foo').length, 0, 'bar foo.length when appended to document' ); + assert_true( !testEl.lastChild.properties.namedItem('foo')[0], 'bar foo[0] when appended to document' ); + assert_equals( testEl.lastChild.properties.namedItem('bar').length, 0, 'bar bar.length when appended to document' ); + assert_true( !testEl.lastChild.properties.namedItem('bar')[0], 'bar bar[0] when appended to document' ); + assert_equals( testEl.lastChild.properties.namedItem('baz').length, 1, 'bar baz.length when appended to document' ); + assert_equals( testEl.lastChild.properties.namedItem('baz')[0], testEl.lastChild.firstChild, 'bar baz[0] when appended to document' ); + assert_true( !testEl.lastChild.properties.namedItem('baz')[1], 'bar baz[1] when appended to document' ); + assert_equals( testEl.lastChild.properties.namedItem('qux').length, 1, 'bar qux.length when appended to document' ); + assert_equals( testEl.lastChild.properties.namedItem('qux')[0], testEl.lastChild.lastChild, 'bar qux[0] when appended to document' ); + assert_true( !testEl.lastChild.properties.namedItem('qux')[1], 'bar qux[1] when appended to document' ); + assert_equals( testEl.lastChild.properties.names.length, 2, 'bar names.length when appended to document' ); + assert_equals( testEl.lastChild.properties.names[0], 'baz', 'bar names[0] when appended to document' ); + assert_equals( testEl.lastChild.properties.names[1], 'qux', 'bar names[1] when appended to document' ); + assert_true( !testEl.lastChild.properties.names[2], 'bar names[2] when appended to document' ); + assert_equals( testEl.lastChild.lastChild.properties.length, 1, 'qux length when appended to document' ); + assert_equals( testEl.lastChild.lastChild.properties[0], testEl.lastChild, 'qux properties[0] when appended to document' ); + assert_true( !testEl.lastChild.lastChild.properties[1], 'qux properties[1] when appended to document' ); + assert_equals( testEl.lastChild.lastChild.properties.namedItem('foo').length, 0, 'qux foo.length when appended to document' ); + assert_true( !testEl.lastChild.lastChild.properties.namedItem('foo')[0], 'qux foo[0] when appended to document' ); + assert_equals( testEl.lastChild.lastChild.properties.namedItem('bar').length, 1, 'qux bar.length when appended to document' ); + assert_equals( testEl.lastChild.lastChild.properties.namedItem('bar')[0], testEl.lastChild, 'qux bar[0] when appended to document' ); + assert_true( !testEl.lastChild.lastChild.properties.namedItem('bar')[1], 'qux bar[1] when appended to document' ); + assert_equals( testEl.lastChild.lastChild.properties.namedItem('baz').length, 0, 'qux baz.length when appended to document' ); + assert_true( !testEl.lastChild.lastChild.properties.namedItem('baz')[0], 'qux baz[0] when appended to document' ); + assert_equals( testEl.lastChild.lastChild.properties.namedItem('qux').length, 0, 'qux qux.length when appended to document' ); + assert_true( !testEl.lastChild.lastChild.properties.namedItem('qux')[0], 'qux qux[0] when appended to document' ); + assert_equals( testEl.lastChild.lastChild.properties.names.length, 1, 'qux names.length when appended to document' ); + assert_equals( testEl.lastChild.lastChild.properties.names[0], 'bar', 'qux names[0] when appended to document' ); + assert_true( !testEl.lastChild.lastChild.properties.names[1], 'qux names[1] when appended to document' ); + } catch(e) { + document.body.removeChild(testEl); + throw (e); + } + document.body.removeChild(testEl); +}, 'itemref referencing parent item'); +test(function () { + //foo has one property, bar + var testEl = makeEl('div',{id:'id1'},'<div itemprop="bar"></div><div itemscope itemref="id1" itemprop="foo"></div>'); + assert_equals( testEl.lastChild.properties.length, 1, 'length' ); + assert_equals( testEl.lastChild.properties[0], testEl.firstChild, 'properties[0]' ); + assert_true( !testEl.lastChild.properties[1], 'properties[1]' ); + assert_equals( testEl.lastChild.properties.namedItem('foo').length, 0, 'foo.length' ); + assert_true( !testEl.lastChild.properties.namedItem('foo')[0], 'foo[0]' ); + assert_equals( testEl.lastChild.properties.namedItem('bar').length, 1, 'bar.length' ); + assert_equals( testEl.lastChild.properties.namedItem('bar')[0], testEl.firstChild, 'bar[0]' ); + assert_true( !testEl.lastChild.properties.namedItem('bar')[1], 'bar[1]' ); + assert_equals( testEl.lastChild.properties.names.length, 1, 'names.length' ); + assert_equals( testEl.lastChild.properties.names[0], 'bar', 'names[0]' ); + assert_true( !testEl.lastChild.properties.names[1], 'names[1]' ); + document.body.appendChild(testEl); + try { + assert_equals( testEl.lastChild.properties.length, 1, 'length when appended to document' ); + assert_equals( testEl.lastChild.properties[0], testEl.firstChild, 'properties[0] when appended to document' ); + assert_true( !testEl.lastChild.properties[1], 'properties[1] when appended to document' ); + assert_equals( testEl.lastChild.properties.namedItem('foo').length, 0, 'foo.length when appended to document' ); + assert_true( !testEl.lastChild.properties.namedItem('foo')[0], 'foo[0] when appended to document' ); + assert_equals( testEl.lastChild.properties.namedItem('bar').length, 1, 'bar.length when appended to document' ); + assert_equals( testEl.lastChild.properties.namedItem('bar')[0], testEl.firstChild, 'bar[0] when appended to document' ); + assert_true( !testEl.lastChild.properties.namedItem('bar')[1], 'bar[1] when appended to document' ); + assert_equals( testEl.lastChild.properties.names.length, 1, 'names.length when appended to document' ); + assert_equals( testEl.lastChild.properties.names[0], 'bar', 'names[0] when appended to document' ); + assert_true( !testEl.lastChild.properties.names[1], 'names[1] when appended to document' ); + } catch(e) { + document.body.removeChild(testEl); + throw (e); + } + document.body.removeChild(testEl); +}, 'itemref referencing parent without itemscope'); +test(function () { + var testDiv = makeEl('div', {itemprop:'bar', id:'foo'}, ''); + var testSpan = makeEl('span', {itemscope:'itemscope', itemref: 'foo', id: 'foo'}, ''); + document.body.appendChild(testDiv); + document.body.appendChild(testSpan); + assert_equals(testSpan.properties.length, 1, 'has one property'); + assert_equals(testSpan.properties[0], testDiv, 'has first property'); + assert_equals(testSpan.properties.item(0), testDiv, 'has first property'); + assert_equals(testSpan.properties.namedItem('bar').length, 1, 'has 1 foo property'); + assert_equals(testSpan.properties.namedItem('bar').item(0), testDiv, 'div is foo property'); + assert_equals(testSpan.properties.names.length, 1, 'only has one property'); + document.body.removeChild(testDiv); + document.body.removeChild(testSpan); +}, 'itemref referencing element with same id'); +test(function () { + //Root has three properties, foo, bar and baz + //Foo has two properties, bar and baz + var testEl = makeEl('div',{itemscope:'itemscope',itemref:'id1'},'<div itemscope itemprop="foo"><div itemprop="bar" id="id1"><div itemprop="baz"></div></div></div>'); + assert_equals( testEl.properties.length, 3, 'outer length' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'outer properties[0]' ); + assert_equals( testEl.properties[1], testEl.firstChild.firstChild, 'outer properties[1]' ); + assert_equals( testEl.properties[2], testEl.firstChild.firstChild.firstChild, 'outer properties[2]' ); + assert_true( !testEl.properties[3], 'outer properties[3]' ); + assert_equals( testEl.properties.namedItem('foo').length, 1, 'outer foo.length' ); + assert_equals( testEl.properties.namedItem('foo')[0], testEl.firstChild, 'outer foo[0]' ); + assert_true( !testEl.properties.namedItem('foo')[1], 'outer foo[1]' ); + assert_equals( testEl.properties.namedItem('bar').length, 1, 'outer bar.length' ); + assert_equals( testEl.properties.namedItem('bar')[0], testEl.firstChild.firstChild, 'outer bar[0]' ); + assert_true( !testEl.properties.namedItem('bar')[1], 'outer bar[1]' ); + assert_equals( testEl.properties.namedItem('baz').length, 1, 'outer baz.length' ); + assert_equals( testEl.properties.namedItem('baz')[0], testEl.firstChild.firstChild.firstChild, 'outer baz[0]' ); + assert_true( !testEl.properties.namedItem('baz')[1], 'outer baz[1]' ); + assert_equals( testEl.properties.names.length, 3, 'outer names.length' ); + assert_equals( testEl.properties.names[0], 'foo', 'outer names[0]' ); + assert_equals( testEl.properties.names[1], 'bar', 'outer names[1]' ); + assert_equals( testEl.properties.names[2], 'baz', 'outer names[2]' ); + assert_true( !testEl.properties.names[3], 'outer names[3]' ); + assert_equals( testEl.firstChild.properties.length, 2, 'inner length' ); + assert_equals( testEl.firstChild.properties[0], testEl.firstChild.firstChild, 'inner properties[0]' ); + assert_equals( testEl.firstChild.properties[1], testEl.firstChild.firstChild.firstChild, 'inner properties[1]' ); + assert_true( !testEl.firstChild.properties[2], 'inner properties[2]' ); + assert_equals( testEl.firstChild.properties.namedItem('foo').length, 0, 'inner foo.length' ); + assert_true( !testEl.firstChild.properties.namedItem('foo')[0], 'inner foo[0]' ); + assert_equals( testEl.firstChild.properties.namedItem('bar').length, 1, 'inner bar.length' ); + assert_equals( testEl.firstChild.properties.namedItem('bar')[0], testEl.firstChild.firstChild, 'inner bar[0]' ); + assert_true( !testEl.firstChild.properties.namedItem('bar')[1], 'inner bar[1]' ); + assert_equals( testEl.firstChild.properties.namedItem('baz').length, 1, 'inner baz.length' ); + assert_equals( testEl.firstChild.properties.namedItem('baz')[0], testEl.firstChild.firstChild.firstChild, 'inner baz[0]' ); + assert_true( !testEl.firstChild.properties.namedItem('baz')[1], 'inner baz[1]' ); + assert_equals( testEl.firstChild.properties.names.length, 2, 'inner names.length' ); + assert_equals( testEl.firstChild.properties.names[0], 'bar', 'inner names[0]' ); + assert_equals( testEl.firstChild.properties.names[1], 'baz', 'inner names[1]' ); + assert_true( !testEl.firstChild.properties.names[2], 'inner names[2]' ); + document.body.appendChild(testEl); + try { + assert_equals( testEl.properties.length, 3, 'outer length when appended to document' ); + assert_equals( testEl.properties[0], testEl.firstChild, 'outer properties[0] when appended to document' ); + assert_equals( testEl.properties[1], testEl.firstChild.firstChild, 'outer properties[1] when appended to document' ); + assert_equals( testEl.properties[2], testEl.firstChild.firstChild.firstChild, 'outer properties[2] when appended to document' ); + assert_true( !testEl.properties[3], 'outer properties[3] when appended to document' ); + assert_equals( testEl.properties.namedItem('foo').length, 1, 'outer foo.length when appended to document' ); + assert_equals( testEl.properties.namedItem('foo')[0], testEl.firstChild, 'outer foo[0] when appended to document' ); + assert_true( !testEl.properties.namedItem('foo')[1], 'outer foo[1] when appended to document' ); + assert_equals( testEl.properties.namedItem('bar').length, 1, 'outer bar.length when appended to document' ); + assert_equals( testEl.properties.namedItem('bar')[0], testEl.firstChild.firstChild, 'outer bar[0] when appended to document' ); + assert_true( !testEl.properties.namedItem('bar')[1], 'outer bar[1] when appended to document' ); + assert_equals( testEl.properties.namedItem('baz').length, 1, 'outer baz.length when appended to document' ); + assert_equals( testEl.properties.namedItem('baz')[0], testEl.firstChild.firstChild.firstChild, 'outer baz[0] when appended to document' ); + assert_true( !testEl.properties.namedItem('baz')[1], 'outer baz[1] when appended to document' ); + assert_equals( testEl.properties.names.length, 3, 'outer names.length when appended to document' ); + assert_equals( testEl.properties.names[0], 'foo', 'outer names[0] when appended to document' ); + assert_equals( testEl.properties.names[1], 'bar', 'outer names[1] when appended to document' ); + assert_equals( testEl.properties.names[2], 'baz', 'outer names[2] when appended to document' ); + assert_true( !testEl.properties.names[3], 'outer names[3] when appended to document' ); + assert_equals( testEl.firstChild.properties.length, 2, 'inner length when appended to document' ); + assert_equals( testEl.firstChild.properties[0], testEl.firstChild.firstChild, 'inner properties[0] when appended to document' ); + assert_equals( testEl.firstChild.properties[1], testEl.firstChild.firstChild.firstChild, 'inner properties[1] when appended to document' ); + assert_true( !testEl.firstChild.properties[2], 'inner properties[2] when appended to document' ); + assert_equals( testEl.firstChild.properties.namedItem('foo').length, 0, 'inner foo.length when appended to document' ); + assert_true( !testEl.firstChild.properties.namedItem('foo')[0], 'inner foo[0] when appended to document' ); + assert_equals( testEl.firstChild.properties.namedItem('bar').length, 1, 'inner bar.length when appended to document' ); + assert_equals( testEl.firstChild.properties.namedItem('bar')[0], testEl.firstChild.firstChild, 'inner bar[0] when appended to document' ); + assert_true( !testEl.firstChild.properties.namedItem('bar')[1], 'inner bar[1] when appended to document' ); + assert_equals( testEl.firstChild.properties.namedItem('baz').length, 1, 'inner baz.length when appended to document' ); + assert_equals( testEl.firstChild.properties.namedItem('baz')[0], testEl.firstChild.firstChild.firstChild, 'inner baz[0] when appended to document' ); + assert_true( !testEl.firstChild.properties.namedItem('baz')[1], 'inner baz[1] when appended to document' ); + assert_equals( testEl.firstChild.properties.names.length, 2, 'inner names.length when appended to document' ); + assert_equals( testEl.firstChild.properties.names[0], 'bar', 'inner names[0] when appended to document' ); + assert_equals( testEl.firstChild.properties.names[1], 'baz', 'inner names[1] when appended to document' ); + assert_true( !testEl.firstChild.properties.names[2], 'inner names[2] when appended to document' ); + } catch(e) { + document.body.removeChild(testEl); + throw (e); + } + document.body.removeChild(testEl); +}, 'itemref pointing to child of nested itemscope'); + + </script> + </body> +</html> -- GitLab