// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. library ElementTest; import 'package:expect/legacy/async_minitest.dart'; // ignore: deprecated_member_use import 'package:expect/expect.dart'; import 'dart:async'; import 'dart:html'; import 'dart:svg' as svg; import 'utils.dart'; expectLargeRect(Rectangle rect) { expect(rect.top, 0); expect(rect.left, 0); expect(rect.width, greaterThan(100)); expect(rect.height, greaterThan(100)); expect(rect.bottom, rect.top + rect.height); expect(rect.right, rect.left + rect.width); } void testUnsupported(String name, void f()) { test(name, () => Expect.throwsUnsupportedError(f)); } main() { var isHRElement = predicate((x) => x is HRElement, 'is a HRElement'); var isBRElement = predicate((x) => x is BRElement, 'is a BRElement'); var isInputElement = predicate( (x) => x is InputElement, 'is an InputElement', ); var isImageElement = predicate( (x) => x is ImageElement, 'is an ImageElement', ); var isSpanElement = predicate((x) => x is SpanElement, 'is a SpanElement'); var isAnchorElement = predicate( (x) => x is AnchorElement, 'is an AnchorElement', ); var isElementList = predicate( (x) => x is List, 'is a List', ); var isElementIterable = predicate( (x) => x is Iterable, 'is an Iterable', ); var isHeadingElement = predicate( (x) => x is HeadingElement, 'is a HeadingElement', ); Element makeElement() => new Element.tag('div'); Element makeElementWithChildren() => new Element.html("

"); group('position', () { test('computedStyle', () { final element = document.body!; var style = element.getComputedStyle(); expect(style.getPropertyValue('left'), 'auto'); }); test('client position synchronous', () { final container = new Element.tag("div"); container.style.position = 'absolute'; container.style.top = '8px'; container.style.left = '9px'; final element = new Element.tag("div"); element.style.width = '200px'; element.style.height = '200px'; container.children.add(element); document.body!.children.add(container); expect(element.client.width, greaterThan(100)); expect(element.client.height, greaterThan(100)); expect(element.offset.width, greaterThan(100)); expect(element.offset.height, greaterThan(100)); expect(element.scrollWidth, greaterThan(100)); expect(element.scrollHeight, greaterThan(100)); expect(element.getBoundingClientRect().left, 9); expect(element.getBoundingClientRect().top, 8); expect(element.documentOffset.x, 9); expect(element.documentOffset.y, 8); container.remove(); }); }); group('constructors', () { test('error', () { Expect.throwsStateError(() => new Element.html('

')); }); test( '.html has no parent', () => expect(new Element.html('
').parent, isNull), ); test('.html table', () { // http://developers.whatwg.org/tabular-data.html#tabular-data TableElement node = new Element.html('''
Characteristics with positive and negative sides
Negative Characteristic Positive
Sad Mood Happy
Failing Grade Passing
''') as TableElement; expect(node.tagName, 'TABLE'); expect(node.parent, isNull); expect( node.caption!.innerHtml, 'Characteristics with positive and negative sides', ); expect(node.tHead!.rows.length, 1); expect(node.tHead!.rows[0].cells.length, 3); expect(node.tBodies.length, 1); expect(node.tBodies[0].rows.length, 2); expect(node.tBodies[0].rows[1].cells.map((c) => c.innerHtml), [ ' Failing\n ', ' Grade\n ', ' Passing\n', ]); }); test('.html caption', () { var table = new TableElement(); TableCaptionElement node = table.createFragment('

Table 1.').nodes.single as TableCaptionElement; expect(node.tagName, 'CAPTION'); expect(node.parent, isNull); expect(node.innerHtml, '

Table 1.

'); }); test('.html colgroup', () { var table = new TableElement(); TableColElement node = table.createFragment(' ').nodes.single as TableColElement; expect(node.tagName, 'COLGROUP'); expect(node.parent, isNull); expect(node.innerHtml, ' '); }); test('.html tbody', () { var innerHtml = 'SadHappy'; var table = new TableElement(); TableSectionElement node = table.createFragment('$innerHtml').nodes.single as TableSectionElement; expect(node.tagName, 'TBODY'); expect(node.parent, isNull); expect(node.rows.length, 1); expect(node.rows[0].cells.length, 2); expect(node.innerHtml, innerHtml); }); test('.html thead', () { var innerHtml = 'NegativePositive'; var table = new TableElement(); TableSectionElement node = table.createFragment('$innerHtml').nodes.single as TableSectionElement; expect(node.tagName, 'THEAD'); expect(node.parent, isNull); expect(node.rows.length, 1); expect(node.rows[0].cells.length, 2); expect(node.innerHtml, innerHtml); }); test('.html tfoot', () { var innerHtml = 'percentage34.3%'; var table = new TableElement(); TableSectionElement node = table.createFragment('$innerHtml').nodes.single as TableSectionElement; expect(node.tagName, 'TFOOT'); expect(node.parent, isNull); expect(node.rows.length, 1); expect(node.rows[0].cells.length, 2); expect(node.innerHtml, innerHtml); }); test('.html tr', () { var table = new TableElement(); document.body!.append(table); var tBody = table.createTBody(); TableRowElement node = tBody.createFragment('foobar').nodes.single as TableRowElement; expect(node.tagName, 'TR'); expect(node.parent, isNull); expect(node.cells.map((c) => c.innerHtml), ['foo', 'bar']); }); test('.html td', () { var table = new TableElement(); document.body!.append(table); var tBody = table.createTBody(); var tRow = tBody.addRow(); TableCellElement node = tRow.createFragment('foobar').nodes.single as TableCellElement; expect(node.tagName, 'TD'); expect(node.parent, isNull); expect(node.innerHtml, 'foobar'); }); test('.html th', () { var table = new TableElement(); document.body!.append(table); var tBody = table.createTBody(); var tRow = tBody.addRow(); TableCellElement node = tRow.createFragment('foobar').nodes.single as TableCellElement; expect(node.tagName, 'TH'); expect(node.parent, isNull); expect(node.innerHtml, 'foobar'); }); test('.html can fire events', () { var e = new Element.html(''); var gotEvent = false; e.onClick.listen((_) { gotEvent = true; }); e.click(); expect(gotEvent, isTrue, reason: 'click should have raised click event'); }); }); group('eventListening', () { test('streams', () { final target = new TextAreaElement(); void testEvent(Stream stream, String type, [createEvent(String type)?]) { var firedOnEvent = false; stream.listen((e) { firedOnEvent = true; }); expect(firedOnEvent, isFalse); var event = createEvent != null ? createEvent(type) : new Event(type); target.dispatchEvent(event); expect(firedOnEvent, isTrue); } testEvent(target.onAbort, 'abort'); testEvent(target.onBeforeCopy, 'beforecopy'); testEvent(target.onBeforeCut, 'beforecut'); testEvent(target.onBeforePaste, 'beforepaste'); testEvent(target.onBlur, 'blur'); testEvent(target.onChange, 'change'); testEvent( target.onContextMenu, 'contextmenu', (type) => new MouseEvent(type), ); // We cannot test dispatching a true ClipboardEvent as the DOM does not // provide a way to create a fake ClipboardEvent. testEvent(target.onCopy, 'copy'); testEvent(target.onCut, 'cut'); testEvent(target.onPaste, 'paste'); testEvent( target.onDoubleClick, 'dblclick', (type) => new MouseEvent(type), ); testEvent(target.onDrag, 'drag', (type) => new MouseEvent(type)); testEvent(target.onDragEnd, 'dragend', (type) => new MouseEvent(type)); testEvent( target.onDragEnter, 'dragenter', (type) => new MouseEvent(type), ); testEvent( target.onDragLeave, 'dragleave', (type) => new MouseEvent(type), ); testEvent(target.onDragOver, 'dragover', (type) => new MouseEvent(type)); testEvent( target.onDragStart, 'dragstart', (type) => new MouseEvent(type), ); testEvent(target.onDrop, 'drop', (type) => new MouseEvent(type)); testEvent(target.onError, 'error'); testEvent(target.onFocus, 'focus'); testEvent(target.onFullscreenChange, 'webkitfullscreenchange'); testEvent(target.onInput, 'input'); testEvent(target.onInvalid, 'invalid'); testEvent(target.onKeyDown, 'keydown', (type) => new KeyboardEvent(type)); testEvent( target.onKeyPress, 'keypress', (type) => new KeyboardEvent(type), ); testEvent(target.onKeyUp, 'keyup', (type) => new KeyboardEvent(type)); testEvent(target.onLoad, 'load'); testEvent( target.onMouseDown, 'mousedown', (type) => new MouseEvent(type), ); testEvent( target.onMouseMove, 'mousemove', (type) => new MouseEvent(type), ); testEvent(target.onMouseOut, 'mouseout', (type) => new MouseEvent(type)); testEvent( target.onMouseOver, 'mouseover', (type) => new MouseEvent(type), ); testEvent(target.onMouseUp, 'mouseup', (type) => new MouseEvent(type)); testEvent(target.onReset, 'reset'); testEvent(target.onScroll, 'scroll'); testEvent(target.onSearch, 'search'); testEvent(target.onSelect, 'select'); testEvent(target.onSelectStart, 'selectstart'); testEvent(target.onSubmit, 'submit'); // We would prefer to create new touch events for this test via // new TouchEvent(null, null, null, type) // but that fails on desktop browsers as touch is not enabled. testEvent(target.onTouchCancel, 'touchcancel'); testEvent(target.onTouchEnd, 'touchend'); testEvent(target.onTouchLeave, 'touchleave'); testEvent(target.onTouchMove, 'touchmove'); testEvent(target.onTouchStart, 'touchstart'); }); }); group('click', () { test('clickEvent', () { var e = new DivElement(); var firedEvent = false; e.onClick.listen((event) { firedEvent = true; }); expect(firedEvent, false); e.click(); expect(firedEvent, true); var e2 = new DivElement(); var firedEvent2 = false; e2.onClick.matches('.foo').listen((event) { firedEvent2 = true; }); e2.click(); expect(firedEvent2, false); e2.classes.add('foo'); e2.click(); expect(firedEvent2, true); }); }); group('attributes', () { test('manipulation', () { final element = new Element.html( '''
''', treeSanitizer: new NullTreeSanitizer(), ); final attributes = element.attributes; expect(attributes['class'], 'foo'); startsWith(match) => predicate((x) => x is String && x.startsWith(match)); expect(attributes['style'], startsWith('overflow: hidden')); expect(attributes['data-foo'], 'bar'); expect(attributes['data-foo2'], 'bar2'); expect(attributes.length, 5); expect(element.dataset.length, 2); element.dataset['foo'] = 'baz'; expect(element.dataset['foo'], 'baz'); expect(attributes['data-foo'], 'baz'); attributes['data-foo2'] = 'baz2'; expect(attributes['data-foo2'], 'baz2'); expect(element.dataset['foo2'], 'baz2'); expect(attributes['dir'], 'rtl'); final dataset = element.dataset; dataset.remove('foo2'); expect(attributes.length, 4); expect(dataset.length, 1); attributes.remove('style'); expect(attributes.length, 3); dataset['foo3'] = 'baz3'; expect(dataset.length, 2); expect(attributes.length, 4); attributes['style'] = 'width: 300px;'; expect(attributes.length, 5); }); test('namespaces', () { var element = new svg.SvgElement.svg( ''' ''', ).children[0]; var attributes = element.attributes; expect(attributes.length, 1); expect(attributes['data-foo'], 'bar'); var xlinkAttrs = element.getNamespacedAttributes( 'http://www.w3.org/1999/xlink', ); expect(xlinkAttrs.length, 1); expect(xlinkAttrs['href'], 'foo'); xlinkAttrs.remove('href'); expect(xlinkAttrs.length, 0); xlinkAttrs['href'] = 'bar'; expect(xlinkAttrs['href'], 'bar'); var randomAttrs = element.getNamespacedAttributes('http://example.com'); expect(randomAttrs.length, 0); randomAttrs['href'] = 'bar'; expect(randomAttrs.length, 1); }); }); group('children', () { test('is a subset of nodes', () { var el = new Element.html("
Foo
"); expect(el.nodes.length, 3); expect(el.children.length, 2); expect(el.nodes[1], el.children[0]); expect(el.nodes[2], el.children[1]); }); test('changes when an element is added to nodes', () { var el = new Element.html("
Foo
"); el.nodes.add(new Element.tag('hr')); expect(el.children.length, 3); expect(el.children[2], isHRElement); expect(el.nodes[3], el.children[2]); }); test('changes nodes when an element is added', () { var el = new Element.html("
Foo
"); el.children.add(new Element.tag('hr')); expect(el.nodes.length, 4); expect(el.nodes[3], isHRElement); expect(el.children[2], el.nodes[3]); }); test('last', () { var el = makeElementWithChildren(); expect(el.children.last, isInputElement); }); test('forEach', () { var els = []; var el = makeElementWithChildren(); el.children.forEach((n) => els.add(n)); expect(els[0], isBRElement); expect(els[1], isImageElement); expect(els[2], isInputElement); }); test('where', () { var filtered = makeElementWithChildren().children.where( (n) => n is ImageElement, ); expect(1, filtered.length); expect(filtered.first, isImageElement); expect(filtered, isElementIterable); }); test('every', () { var el = makeElementWithChildren(); expect(el.children.every((n) => n is Element), isTrue); expect(el.children.every((n) => n is InputElement), isFalse); }); test('any', () { var el = makeElementWithChildren(); expect(el.children.any((n) => n is InputElement), isTrue); expect(el.children.any((n) => n is svg.SvgElement), isFalse); }); test('isEmpty', () { expect(makeElement().children.isEmpty, isTrue); expect(makeElementWithChildren().children.isEmpty, isFalse); }); test('length', () { expect(makeElement().children.length, 0); expect(makeElementWithChildren().children.length, 3); }); test('[]', () { var el = makeElementWithChildren(); expect(el.children[0], isBRElement); expect(el.children[1], isImageElement); expect(el.children[2], isInputElement); }); test('[]=', () { var el = makeElementWithChildren(); el.children[1] = new Element.tag('hr'); expect(el.children[0], isBRElement); expect(el.children[1], isHRElement); expect(el.children[2], isInputElement); }); test('add', () { var el = makeElement(); el.children.add(new Element.tag('hr')); expect(el.children.last, isHRElement); }); test('iterator', () { var els = []; var el = makeElementWithChildren(); for (var subel in el.children) { els.add(subel); } expect(els[0], isBRElement); expect(els[1], isImageElement); expect(els[2], isInputElement); }); test('addAll', () { var el = makeElementWithChildren(); el.children.addAll([ new Element.tag('span'), new Element.tag('a'), new Element.tag('h1'), ]); expect(el.children[0], isBRElement); expect(el.children[1], isImageElement); expect(el.children[2], isInputElement); expect(el.children[3], isSpanElement); expect(el.children[4], isAnchorElement); expect(el.children[5], isHeadingElement); }); test('insert', () { var element = new DivElement(); element.children.insert(0, new BRElement()); expect(element.children[0], isBRElement); element.children.insert(0, new HRElement()); expect(element.children[0], isHRElement); element.children.insert(1, new ImageElement()); expect(element.children[1], isImageElement); element.children.insert(element.children.length, new InputElement()); expect(element.children.last, isInputElement); }); test('clear', () { var el = makeElementWithChildren(); el.children.clear(); expect(el.children, equals([])); }); test('removeLast', () { var el = makeElementWithChildren(); expect(el.children.removeLast(), isInputElement); expect(el.children.length, 2); expect(el.children.removeLast(), isImageElement); expect(el.children.length, 1); }); test('sublist', () { var el = makeElementWithChildren(); expect(el.children.sublist(1, 2), isElementList); }); test('getRange', () { var el = makeElementWithChildren(); expect(el.children.getRange(1, 2).length, 1); }); test('retainWhere', () { var el = makeElementWithChildren(); expect(el.children.length, 3); el.children.retainWhere((e) => true); expect(el.children.length, 3); el = makeElementWithChildren(); expect(el.children.length, 3); el.children.retainWhere((e) => false); expect(el.children.length, 0); el = makeElementWithChildren(); expect(el.children.length, 3); el.children.retainWhere((e) => e.localName == 'input'); expect(el.children.length, 1); el = makeElementWithChildren(); expect(el.children.length, 3); el.children.retainWhere((e) => e.localName == 'br'); expect(el.children.length, 1); }); test('removeWhere', () { var el = makeElementWithChildren(); expect(el.children.length, 3); el.children.removeWhere((e) => true); expect(el.children.length, 0); el = makeElementWithChildren(); expect(el.children.length, 3); el.children.removeWhere((e) => false); expect(el.children.length, 3); el = makeElementWithChildren(); expect(el.children.length, 3); el.children.removeWhere((e) => e.localName == 'input'); expect(el.children.length, 2); el = makeElementWithChildren(); expect(el.children.length, 3); el.children.removeWhere((e) => e.localName == 'br'); expect(el.children.length, 2); }); testUnsupported('sort', () { var l = makeElementWithChildren().children; l.sort(); }); testUnsupported('setRange', () { var l = makeElementWithChildren().children; l.setRange(0, 0, []); }); testUnsupported('replaceRange', () { var l = makeElementWithChildren().children; l.replaceRange(0, 0, []); }); testUnsupported('removeRange', () { var l = makeElementWithChildren().children; l.removeRange(0, 1); }); testUnsupported('insertAll', () { var l = makeElementWithChildren().children; l.insertAll(0, []); }); }); group('matches', () { test('matches', () { var element = new DivElement(); document.body!.append(element); element.classes.add('test'); expect(element.matches('div'), true); expect(element.matches('span'), false); expect(element.matches('.test'), true); }); }); group('querySelectorAll', () { List getQuerySelectorAll() { return new Element.html("""

Dart!

Hello, world!


""").querySelectorAll('.q'); } List getEmptyQuerySelectorAll() => new Element.tag('div').querySelectorAll('img'); test('last', () { expect(getQuerySelectorAll().last, isHRElement); }); test('forEach', () { var els = []; getQuerySelectorAll().forEach((el) => els.add(el)); expect(els[0], isAnchorElement); expect(els[1], isSpanElement); expect(els[2], isHRElement); }); test('map', () { var texts = getQuerySelectorAll().map((el) => el.text).toList(); expect(texts, equals(['Dart!', 'Hello', ''])); }); test('where', () { var filtered = getQuerySelectorAll() .where((n) => n is SpanElement) .toList(); expect(filtered.length, 1); expect(filtered[0], isSpanElement); expect(filtered, isElementList); }); test('every', () { var el = getQuerySelectorAll(); expect(el.every((n) => n is Element), isTrue); expect(el.every((n) => n is SpanElement), isFalse); }); test('any', () { var el = getQuerySelectorAll(); expect(el.any((n) => n is SpanElement), isTrue); expect(el.any((n) => n is svg.SvgElement), isFalse); }); test('isEmpty', () { expect(getEmptyQuerySelectorAll().isEmpty, isTrue); expect(getQuerySelectorAll().isEmpty, isFalse); }); test('length', () { expect(getEmptyQuerySelectorAll().length, 0); expect(getQuerySelectorAll().length, 3); }); test('[]', () { var els = getQuerySelectorAll(); expect(els[0], isAnchorElement); expect(els[1], isSpanElement); expect(els[2], isHRElement); }); test('iterator', () { var els = []; for (var subel in getQuerySelectorAll()) { els.add(subel); } expect(els[0], isAnchorElement); expect(els[1], isSpanElement); expect(els[2], isHRElement); }); test('sublist', () { expect(getQuerySelectorAll().sublist(1, 2) is List, isTrue); }); testUnsupported( '[]=', () => getQuerySelectorAll()[1] = new Element.tag('br'), ); testUnsupported( 'add', () => getQuerySelectorAll().add(new Element.tag('br')), ); testUnsupported('addAll', () { getQuerySelectorAll().addAll([ new Element.tag('span'), new Element.tag('a'), new Element.tag('h1'), ]); }); testUnsupported('sort', () => getQuerySelectorAll().sort((a1, a2) => 0)); testUnsupported('setRange', () { getQuerySelectorAll().setRange(0, 1, [new BRElement()]); }); testUnsupported( 'removeRange', () => getQuerySelectorAll().removeRange(0, 1), ); testUnsupported('clear', () => getQuerySelectorAll().clear()); testUnsupported('removeLast', () => getQuerySelectorAll().removeLast()); }); group('functional', () { test('toString', () { final elems = makeElementWithChildren().children; expect(elems.toString(), "[br, img, input]"); final elem = makeElement().children; expect(elem.toString(), '[]'); }); test('scrollIntoView', () { var child = new DivElement(); document.body!.append(child); child.scrollIntoView(ScrollAlignment.TOP); child.scrollIntoView(ScrollAlignment.BOTTOM); child.scrollIntoView(ScrollAlignment.CENTER); child.scrollIntoView(); }); }); group('_ElementList', () { List makeElList() => makeElementWithChildren().children; test('where', () { var filtered = makeElList().where((n) => n is ImageElement); expect(filtered.length, 1); expect(filtered.first, isImageElement); expect(filtered, isElementIterable); }); test('sublist', () { var range = makeElList().sublist(1, 3); expect(range, isElementList); expect(range[0], isImageElement); expect(range[1], isInputElement); }); }); group('eventDelegation', () { test('matches', () { Element clickOne = new Element.a(); Element selectorOne = new Element.div() ..classes.add('selector') ..children.add(clickOne); Element clickTwo = new Element.a(); Element selectorTwo = new Element.div() ..classes.add('selector') ..children.add(clickTwo); document.body!.append(selectorOne); document.body!.append(selectorTwo); document.body!.onClick .matches('.selector') .listen( expectAsync1((Event event) { expect(event.currentTarget, document.body); expect(event.target, clickOne); expect(event.matchingTarget, selectorOne); }), ); selectorOne.onClick .matches('.selector') .listen( expectAsync1((Event event) { expect(event.currentTarget, selectorOne); expect(event.target, clickOne); expect(event.matchingTarget, selectorOne); }), ); clickOne.click(); Element elem = new Element.div()..classes.addAll(['a', 'b']); Element img = new Element.img() ..classes.addAll(['b', 'a', 'd']) ..id = 'cookie'; Element input = new InputElement()..classes.addAll(['c', 'd']); var div = new Element.div() ..classes.add('a') ..id = 'wat'; document.body!.append(elem); document.body!.append(img); document.body!.append(input); document.body!.append(div); Element elem4 = new Element.div()..classes.addAll(['i', 'j']); Element elem5 = new Element.div() ..classes.addAll(['g', 'h']) ..children.add(elem4); Element elem6 = new Element.div() ..classes.addAll(['e', 'f']) ..children.add(elem5); document.body!.append(elem6); var firedEvent = false; var elems = querySelectorAll('.a'); querySelectorAll('.a').onClick.listen((event) { firedEvent = true; }); expect(firedEvent, false); querySelector('.c')!.click(); expect(firedEvent, false); querySelector('#wat')!.click(); expect(firedEvent, true); var firedEvent4 = false; querySelectorAll('.a').onClick.matches('.d').listen((event) { firedEvent4 = true; }); expect(firedEvent4, false); querySelector('.c')!.click(); expect(firedEvent4, false); querySelector('#wat')!.click(); expect(firedEvent4, false); querySelector('#cookie')!.click(); expect(firedEvent4, true); var firedEvent2 = false; querySelectorAll('.a').onClick.listen((event) { firedEvent2 = true; }); Element elem2 = new Element.html('

'); document.body!.append(elem2); elem2.click(); expect(firedEvent2, false); elem2.classes.add('a'); elem2.click(); expect(firedEvent2, false); var firedEvent3 = false; querySelectorAll(':root').onClick.matches('.a').listen((event) { firedEvent3 = true; }); Element elem3 = new Element.html('

'); document.body!.append(elem3); elem3.click(); expect(firedEvent3, false); elem3.classes.add('a'); elem3.click(); expect(firedEvent3, true); var firedEvent5 = false; querySelectorAll(':root').onClick.matches('.e').listen((event) { firedEvent5 = true; }); expect(firedEvent5, false); querySelector('.i')!.click(); expect(firedEvent5, true); }); test('event ordering', () { var a = new DivElement(); var b = new DivElement(); a.append(b); var c = new DivElement(); b.append(c); var eventOrder = []; ElementStream aEvent = a.on['custom_event'] as ElementStream; aEvent.listen((_) { eventOrder.add('a no-capture'); }); aEvent.capture((_) { eventOrder.add('a capture'); }); ElementStream bEvent = b.on['custom_event'] as ElementStream; bEvent.listen((_) { eventOrder.add('b no-capture'); }); bEvent.capture((_) { eventOrder.add('b capture'); }); document.body!.append(a); var event = new Event('custom_event', canBubble: true); c.dispatchEvent(event); expect(eventOrder, [ 'a capture', 'b capture', 'b no-capture', 'a no-capture', ]); }); }); group('ElementList', () { // Tests for methods on the DOM class 'NodeList'. // // There are two interesting things that are validated here from the // viewpoint of the dart2js implementation of a 'native' class: // // 1. Some methods are implemented from by 'Object' or 'Interceptor'; // some of these tests validate that a method can be called. // 2. Some methods are implemented by mixins. ElementList makeElementList() => (new Element.html( "
Foo


", )).querySelectorAll('br'); test('hashCode', () { var nodes = makeElementList(); var hash = nodes.hashCode; final int N = 1000; int matchCount = 0; for (int i = 0; i < N; i++) { if (makeElementList().hashCode == hash) matchCount++; } expect(matchCount, lessThan(N)); }); test('operator==', () { var a = [makeElementList(), makeElementList(), null]; for (int i = 0; i < a.length; i++) { for (int j = 0; j < a.length; j++) { expect(i == j, a[i] == a[j]); } } }); test('runtimeType', () { var nodes1 = makeElementList(); var nodes2 = makeElementList(); var type1 = nodes1.runtimeType; var type2 = nodes2.runtimeType; expect(type1 == type2, true); String name = '$type1'; if (name.length > 3) { expect(name.contains('ElementList'), true); } }); test('first', () { var nodes = makeElementList(); expect(nodes.first, isBRElement); }); test('last', () { var nodes = makeElementList(); expect(nodes.last, isBRElement); }); test('where', () { var filtered = makeElementList().where((n) => n is BRElement).toList(); expect(filtered.length, 3); expect(filtered[0], isBRElement); }); test('sublist', () { var range = makeElementList().sublist(1, 3); expect(range.length, 2); expect(range[0], isBRElement); expect(range[1], isBRElement); }); }); }