blob: cd47c388a6db62ee38a8741b758c23531314a08e [file] [log] [blame]
// 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 DocumentFragmentTest;
import '../../pkg/unittest/lib/unittest.dart';
import '../../pkg/unittest/lib/html_config.dart';
import 'dart:html';
part 'util.dart';
main() {
useHtmlConfiguration();
var isAnchorElement =
predicate((x) => x is AnchorElement, 'is an AnchorElement');
Collection<String> _nodeStrings(Collection<Node> input) {
var out = new List<String>();
for (Node n in input) {
if (n is Element) {
Element e = n;
out.add(e.tagName);
} else {
out.add(n.text);
}
}
return out;
};
assertConstError(void fn()) {
try {
fn();
} catch (e) {
if (e is UnsupportedError) {
return;
}
}
expect(true, isFalse, reason: 'Expected immutability error');
};
void expectEmptyStyleDeclaration(CSSStyleDeclaration style) {
expect(style.cssText, equals(''));
expect(style.getPropertyPriority('color'), equals(''));
expect(style.item(0), equals(''));
expect(style.length, isZero);
// TODO(jacobr): these checks throw UnimplementedErrors in dartium.
// expect(style.parentRule, isNull);
// expect(style.getPropertyCSSValue('color'), isNull);
// expect(style.getPropertyShorthand('color'), isNull);
// expect(style.isPropertyImplicit('color'), isFalse);
// Ideally these would throw errors, but it's not possible to create a class
// that'll intercept these calls without implementing the entire
// CSSStyleDeclaration interface, so we'll settle for them being no-ops.
style.cssText = '* {color: blue}';
style.removeProperty('color');
style.setProperty('color', 'blue');
}
group('constructors', () {
test('0-argument makes an empty fragment', () {
final fragment = new DocumentFragment();
expect(fragment.children, equals([]));
});
test('.html parses input as HTML', () {
final fragment = new DocumentFragment.html('<a>foo</a>');
expect(fragment.children[0], isAnchorElement);
});
// test('.svg parses input as SVG', () {
// final fragment = new DocumentFragment.svg('<a>foo</a>');
// expect(fragment.children[0] is SVGAElement, isTrue);
// });
// TODO(nweiz): enable this once XML is ported.
// test('.xml parses input as XML', () {
// final fragment = new DocumentFragment.xml('<a>foo</a>');
// expect(fragment.children[0] is XMLElement, isTrue);
// });
});
test('Unsupported operations throw errors', () {
var emptyFragment = new DocumentFragment();
expectUnsupported(() => emptyFragment.attributes = {});
expectUnsupported(() => emptyFragment.classes = []);
expectUnsupported(() => emptyFragment.classes.add('foo'));
expectUnsupported(() => emptyFragment.dataAttributes = {});
expectUnsupported(() => emptyFragment.contentEditable = "true");
expectUnsupported(() => emptyFragment.dir);
expectUnsupported(() => emptyFragment.dir = "ltr");
expectUnsupported(() => emptyFragment.draggable = true);
expectUnsupported(() => emptyFragment.hidden = true);
expectUnsupported(() => emptyFragment.id = "foo");
expectUnsupported(() => emptyFragment.lang);
expectUnsupported(() => emptyFragment.lang = "en");
expectUnsupported(() => emptyFragment.scrollLeft = 10);
expectUnsupported(() => emptyFragment.scrollTop = 10);
expectUnsupported(() => emptyFragment.spellcheck = true);
expectUnsupported(() => emptyFragment.translate = true);
expectUnsupported(() => emptyFragment.tabIndex = 5);
expectUnsupported(() => emptyFragment.title = "foo");
expectUnsupported(() => emptyFragment.webkitdropzone = "foo");
expectUnsupported(() => emptyFragment.webkitRegionOverflow = "foo");
});
group('children', () {
var fragment;
var children;
init() {
fragment = new DocumentFragment();
children = fragment.children;
fragment.nodes.addAll(
[new Text("1"), new Element.tag("A"), new Element.tag("B"),
new Text("2"), new Element.tag("I"), new Text("3"),
new Element.tag("U")]);
};
test('is initially empty', () {
children = new DocumentFragment().children;
expect(children, equals([]));
expect(children.isEmpty, isTrue);
});
test('filters out non-element nodes', () {
init();
expect(_nodeStrings(fragment.nodes),
orderedEquals(["1", "A", "B", "2", "I", "3", "U"]));
expect(_nodeStrings(children),
orderedEquals(["A", "B", "I", "U"]));
});
test('only indexes children, not other nodes', () {
init();
children[1] = new Element.tag("BR");
expect(_nodeStrings(fragment.nodes),
orderedEquals(["1", "A", "BR", "2", "I", "3", "U"]));
expect(_nodeStrings(children),
orderedEquals(["A", "BR", "I", "U"]));
});
test('adds to both children and nodes', () {
init();
children.add(new Element.tag("UL"));
expect(_nodeStrings(fragment.nodes),
orderedEquals(["1", "A", "B", "2", "I", "3", "U", "UL"]));
expect(_nodeStrings(children),
orderedEquals(["A", "B", "I", "U", "UL"]));
});
test('removes only children, from both children and nodes', () {
init();
expect(children.removeLast().tagName, equals('U'));
expect(_nodeStrings(fragment.nodes),
orderedEquals(["1", "A", "B", "2", "I", "3"]));
expect(_nodeStrings(children),
orderedEquals(["A", "B", "I"]));
expect(children.removeLast().tagName, "I");
expect(_nodeStrings(fragment.nodes),
equals(["1", "A", "B", "2", "3"]));
expect(_nodeStrings(children), equals(["A", "B"]));
});
test('accessors are wrapped', () {
init();
expect(children[0].tagName, "A");
expect(_nodeStrings(children.filter((e) => e.tagName == "I")), ["I"]);
expect(children.every((e) => e is Element), isTrue);
expect(children.some((e) => e.tagName == "U"), isTrue);
expect(children.isEmpty, isFalse);
expect(children.length, 4);
expect(children[2].tagName, "I");
expect(children.last.tagName, "U");
});
test('setting children overwrites nodes as well', () {
init();
fragment.children = [new Element.tag("DIV"), new Element.tag("HEAD")];
expect(_nodeStrings(fragment.nodes), equals(["DIV", "HEAD"]));
});
});
test('setting innerHTML works', () {
var fragment = new DocumentFragment();
fragment.nodes.add(new Text("foo"));
fragment.innerHTML = "<a>bar</a>baz";
expect(_nodeStrings(fragment.nodes), equals(["A", "baz"]));
});
test('getting innerHTML works', () {
var fragment = new DocumentFragment();
fragment.nodes.addAll([new Text("foo"), new Element.html("<A>bar</A>")]);
expect(fragment.innerHTML, "foo<a>bar</a>");
expect(fragment.outerHTML, "foo<a>bar</a>");
});
group('insertAdjacentElement', () {
getFragment() => new DocumentFragment.html("<a>foo</a>");
test('beforeBegin does nothing', () {
var fragment = getFragment();
expect(fragment.insertAdjacentElement("beforeBegin",
new Element.tag("b")), isNull);
expect(fragment.innerHTML, "<a>foo</a>");
});
test('afterEnd does nothing', () {
var fragment = getFragment();
expect(fragment.insertAdjacentElement("afterEnd",
new Element.tag("b")), isNull);
expect(fragment.innerHTML, "<a>foo</a>");
});
test('afterBegin inserts the element', () {
var fragment = getFragment();
var el = new Element.tag("b");
expect(fragment.insertAdjacentElement("afterBegin", el), equals(el));
expect(fragment.innerHTML, "<b></b><a>foo</a>");
});
test('beforeEnd inserts the element', () {
var fragment = getFragment();
var el = new Element.tag("b");
expect(fragment.insertAdjacentElement("beforeEnd", el), equals(el));
expect(fragment.innerHTML, "<a>foo</a><b></b>");
});
});
group('insertAdjacentText', () {
getFragment() => new DocumentFragment.html("<a>foo</a>");
test('beforeBegin does nothing', () {
var fragment = getFragment();
fragment.insertAdjacentText("beforeBegin", "foo");
expect(fragment.innerHTML, "<a>foo</a>");
});
test('afterEnd does nothing', () {
var fragment = getFragment();
fragment.insertAdjacentText("afterEnd", "foo");
expect(fragment.innerHTML, "<a>foo</a>");
});
test('afterBegin inserts the text', () {
var fragment = getFragment();
fragment.insertAdjacentText("afterBegin", "foo");
expect(fragment.innerHTML, "foo<a>foo</a>");
});
test('beforeEnd inserts the text', () {
var fragment = getFragment();
fragment.insertAdjacentText("beforeEnd", "foo");
expect(fragment.innerHTML, "<a>foo</a>foo");
});
});
group('insertAdjacentHTML', () {
getFragment() => new DocumentFragment.html("<a>foo</a>");
test('beforeBegin does nothing', () {
var fragment = getFragment();
fragment.insertAdjacentHTML("beforeBegin", "foo<br>");
expect(fragment.innerHTML, "<a>foo</a>");
});
test('afterEnd does nothing', () {
var fragment = getFragment();
fragment.insertAdjacentHTML("afterEnd", "<br>foo");
expect(fragment.innerHTML, "<a>foo</a>");
});
test('afterBegin inserts the HTML', () {
var fragment = getFragment();
fragment.insertAdjacentHTML("afterBegin", "foo<br>");
expect(fragment.innerHTML, "foo<br><a>foo</a>");
});
test('beforeEnd inserts the HTML', () {
var fragment = getFragment();
fragment.insertAdjacentHTML("beforeEnd", "<br>foo");
expect(fragment.innerHTML, "<a>foo</a><br>foo");
});
});
// Just test that these methods don't throw errors
test("no-op methods don't throw errors", () {
var fragment = new DocumentFragment();
fragment.on.click.add((e) => null);
fragment.blur();
fragment.focus();
fragment.click();
fragment.scrollByLines(2);
fragment.scrollByPages(2);
fragment.scrollIntoView();
fragment.webkitRequestFullScreen(2);
});
test('default values', () {
var fragment = new DocumentFragment();
expect(fragment.contentEditable, "false");
expect(fragment.tabIndex, -1);
expect(fragment.id, "");
expect(fragment.title, "");
expect(fragment.tagName, "");
expect(fragment.webkitdropzone, "");
expect(fragment.webkitRegionOverflow, "");
expect(fragment.isContentEditable, isFalse);
expect(fragment.draggable, isFalse);
expect(fragment.hidden, isFalse);
expect(fragment.spellcheck, isFalse);
expect(fragment.translate, isFalse);
expect(fragment.nextElementSibling, isNull);
expect(fragment.previousElementSibling, isNull);
expect(fragment.offsetParent, isNull);
expect(fragment.parent, isNull);
expect(fragment.attributes.isEmpty, isTrue);
expect(fragment.classes.isEmpty, isTrue);
expect(fragment.dataAttributes.isEmpty, isTrue);
expect(fragment.matchesSelector("foo"), isFalse);
expect(fragment.matchesSelector("*"), isFalse);
});
test('style', () {
var fragment = new DocumentFragment();
var style = fragment.style;
expectEmptyStyleDeclaration(style);
fragment.computedStyle.then(expectAsync1((computedStyle) {
expectEmptyStyleDeclaration(computedStyle);
}));
});
// TODO(nweiz): re-enable when const is better supported in dartc and/or frog
// test('const fields are immutable', () {
// var fragment = new DocumentFragment();
// assertConstError(() => fragment.attributes['title'] = 'foo');
// assertConstError(() => fragment.dataAttributes['title'] = 'foo');
// fragment.rect.then((ElementRect rect) {
// assertConstError(() => rect.clientRects.add(null));
// callbackDone();
// });
// // Issue 174: #classes is currently not const
// // assertConstError(() => fragment.classes.add('foo'));
// });
test('query searches the fragment', () {
var fragment = new DocumentFragment.html(
"<div class='foo'><a>foo</a><b>bar</b></div>");
expect(fragment.query(".foo a").tagName, "A");
expect(_nodeStrings(fragment.queryAll(".foo *")), equals(["A", "B"]));
});
}