Disable implicit casts (#130)
This will make it easier to migrate to null safety which also disables
implicit casts except from dynamic. Also fix the implicit casts from
dynamic since this package has a lot of dynamic code that is harder to
read with implicit casts.
- Add argument types to some signatures that should have had them from
the start.
- Add explicit casts.
- Extract a few new local variables to make some of the casts only need
to happen once.
- Use `Iterable.whereType` in a loop instead of a type check and
`continue` in the loop body.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 53d7970..615a09f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 0.15.0-dev
+
## 0.14.0+4
- Fix a bug parsing bad HTML where a 'button' end tag needs to close other
diff --git a/analysis_options.yaml b/analysis_options.yaml
index b8dcf93..20eba0c 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1,5 +1,7 @@
include: package:pedantic/analysis_options.yaml
analyzer:
+ strong-mode:
+ implicit-casts: false
errors:
# https://github.com/dart-lang/linter/issues/1649
prefer_collection_literals: ignore
diff --git a/lib/dom.dart b/lib/dom.dart
index 835d8da..85e02c4 100644
--- a/lib/dom.dart
+++ b/lib/dom.dart
@@ -24,7 +24,7 @@
// TODO(jmesserly): this needs to be replaced by an AttributeMap for attributes
// that exposes namespace info.
-class AttributeName implements Comparable {
+class AttributeName implements Comparable<Object> {
/// The namespace prefix, e.g. `xlink`.
final String prefix;
@@ -55,14 +55,15 @@
}
@override
- int compareTo(other) {
+ int compareTo(dynamic other) {
// Not sure about this sort order
if (other is! AttributeName) return 1;
- var cmp = (prefix ?? '').compareTo((other.prefix ?? ''));
+ final otherAttributeName = other as AttributeName;
+ var cmp = (prefix ?? '').compareTo((otherAttributeName.prefix ?? ''));
if (cmp != 0) return cmp;
- cmp = name.compareTo(other.name);
+ cmp = name.compareTo(otherAttributeName.name);
if (cmp != 0) return cmp;
- return namespace.compareTo(other.namespace);
+ return namespace.compareTo(otherAttributeName.namespace);
}
@override
@@ -141,7 +142,10 @@
///
/// Returns null if this node either does not have a parent or its parent is
/// not an element.
- Element get parent => parentNode is Element ? parentNode : null;
+ Element get parent {
+ final parentNode = this.parentNode;
+ return parentNode is Element ? parentNode : null;
+ }
// TODO(jmesserly): should move to Element.
/// A map holding name, value pairs for attributes of the node.
@@ -299,7 +303,7 @@
}
}
- Node _clone(Node shallowClone, bool deep) {
+ T _clone<T extends Node>(T shallowClone, bool deep) {
if (deep) {
for (var child in nodes) {
shallowClone.append(child.clone(true));
@@ -442,7 +446,7 @@
void appendData(String data) {
if (_data is! StringBuffer) _data = StringBuffer(_data);
- StringBuffer sb = _data;
+ final sb = _data as StringBuffer;
sb.write(data);
}
@@ -1054,18 +1058,16 @@
@override
Iterable<Element> getRange(int start, int end) =>
_filtered.getRange(start, end);
- // TODO(sigmund): this should be typed Element, but we currently run into a
- // bug where ListMixin<E>.indexOf() expects Object as the argument.
@override
int indexOf(Object element, [int start = 0]) =>
- _filtered.indexOf(element, start);
+ // Cast forced by ListMixin https://github.com/dart-lang/sdk/issues/31311
+ _filtered.indexOf(element as Element, start);
- // TODO(sigmund): this should be typed Element, but we currently run into a
- // bug where ListMixin<E>.lastIndexOf() expects Object as the argument.
@override
int lastIndexOf(Object element, [int start]) {
start ??= length - 1;
- return _filtered.lastIndexOf(element, start);
+ // Cast forced by ListMixin https://github.com/dart-lang/sdk/issues/31311
+ return _filtered.lastIndexOf(element as Element, start);
}
@override
diff --git a/lib/dom_parsing.dart b/lib/dom_parsing.dart
index f16018c..3c8774b 100644
--- a/lib/dom_parsing.dart
+++ b/lib/dom_parsing.dart
@@ -10,17 +10,17 @@
void visit(Node node) {
switch (node.nodeType) {
case Node.ELEMENT_NODE:
- return visitElement(node);
+ return visitElement(node as Element);
case Node.TEXT_NODE:
- return visitText(node);
+ return visitText(node as Text);
case Node.COMMENT_NODE:
- return visitComment(node);
+ return visitComment(node as Comment);
case Node.DOCUMENT_FRAGMENT_NODE:
- return visitDocumentFragment(node);
+ return visitDocumentFragment(node as DocumentFragment);
case Node.DOCUMENT_NODE:
- return visitDocument(node);
+ return visitDocument(node as Document);
case Node.DOCUMENT_TYPE_NODE:
- return visitDocumentType(node);
+ return visitDocumentType(node as DocumentType);
default:
throw UnsupportedError('DOM node type ${node.nodeType}');
}
diff --git a/lib/parser.dart b/lib/parser.dart
index 46702ae..2a6c431 100644
--- a/lib/parser.dart
+++ b/lib/parser.dart
@@ -318,7 +318,7 @@
// Note: avoid "is" test here, see http://dartbug.com/4795
if (type == TokenKind.parseError) {
- ParseErrorToken error = newToken;
+ final error = newToken as ParseErrorToken;
parseError(error.span, error.data, error.messageParams);
newToken = null;
} else {
@@ -329,22 +329,24 @@
switch (type) {
case TokenKind.characters:
- newToken = localPhase.processCharacters(newToken);
+ newToken =
+ localPhase.processCharacters(newToken as CharactersToken);
break;
case TokenKind.spaceCharacters:
- newToken = localPhase.processSpaceCharacters(newToken);
+ newToken = localPhase
+ .processSpaceCharacters(newToken as SpaceCharactersToken);
break;
case TokenKind.startTag:
- newToken = localPhase.processStartTag(newToken);
+ newToken = localPhase.processStartTag(newToken as StartTagToken);
break;
case TokenKind.endTag:
- newToken = localPhase.processEndTag(newToken);
+ newToken = localPhase.processEndTag(newToken as EndTagToken);
break;
case TokenKind.comment:
- newToken = localPhase.processComment(newToken);
+ newToken = localPhase.processComment(newToken as CommentToken);
break;
case TokenKind.doctype:
- newToken = localPhase.processDoctype(newToken);
+ newToken = localPhase.processDoctype(newToken as DoctypeToken);
break;
}
}
@@ -571,7 +573,7 @@
void parseRCDataRawtext(Token token, String contentType) {
assert(contentType == 'RAWTEXT' || contentType == 'RCDATA');
- tree.insertElement(token);
+ tree.insertElement(token as StartTagToken);
if (contentType == 'RAWTEXT') {
tokenizer.state = tokenizer.rawtextState;
@@ -665,7 +667,7 @@
}
class InitialPhase extends Phase {
- InitialPhase(parser) : super(parser);
+ InitialPhase(HtmlParser parser) : super(parser);
@override
Token processSpaceCharacters(SpaceCharactersToken token) {
@@ -824,7 +826,7 @@
}
class BeforeHtmlPhase extends Phase {
- BeforeHtmlPhase(parser) : super(parser);
+ BeforeHtmlPhase(HtmlParser parser) : super(parser);
// helper methods
void insertHtmlElement() {
@@ -885,7 +887,7 @@
}
class BeforeHeadPhase extends Phase {
- BeforeHeadPhase(parser) : super(parser);
+ BeforeHeadPhase(HtmlParser parser) : super(parser);
@override
Token processStartTag(StartTagToken token) {
@@ -959,7 +961,7 @@
}
class InHeadPhase extends Phase {
- InHeadPhase(parser) : super(parser);
+ InHeadPhase(HtmlParser parser) : super(parser);
@override
Token processStartTag(StartTagToken token) {
@@ -1106,7 +1108,7 @@
// class InHeadNoScriptPhase extends Phase {
class AfterHeadPhase extends Phase {
- AfterHeadPhase(parser) : super(parser);
+ AfterHeadPhase(HtmlParser parser) : super(parser);
@override
Token processStartTag(StartTagToken token) {
@@ -1182,7 +1184,7 @@
void startTagFromHead(StartTagToken token) {
parser.parseError(token.span, 'unexpected-start-tag-out-of-my-head',
{'name': token.name});
- tree.openElements.add(tree.headPointer);
+ tree.openElements.add(tree.headPointer as Element);
parser._inHeadPhase.processStartTag(token);
for (var node in tree.openElements.reversed) {
if (node.localName == 'head') {
@@ -1225,7 +1227,7 @@
// http://www.whatwg.org/specs/web-apps/current-work///parsing-main-inbody
// the really-really-really-very crazy mode
- InBodyPhase(parser) : super(parser);
+ InBodyPhase(HtmlParser parser) : super(parser);
@override
Token processStartTag(StartTagToken token) {
@@ -1501,7 +1503,7 @@
}
// helper
- void addFormattingElement(token) {
+ void addFormattingElement(StartTagToken token) {
tree.insertElement(token);
var element = tree.openElements.last;
@@ -1509,7 +1511,7 @@
for (Node node in tree.activeFormattingElements.reversed) {
if (node == Marker) {
break;
- } else if (isMatchingFormattingElement(node, element)) {
+ } else if (isMatchingFormattingElement(node as Element, element)) {
matchingElements.add(node);
}
}
@@ -1816,7 +1818,7 @@
if (tree.formPointer != null) {
return;
}
- var formAttrs = <dynamic, String>{};
+ var formAttrs = LinkedHashMap<dynamic, String>();
var dataAction = token.data['action'];
if (dataAction != null) {
formAttrs['action'] = dataAction;
@@ -2113,7 +2115,7 @@
// Step 2
// Start of the adoption agency algorithm proper
var afeIndex = tree.openElements.indexOf(formattingElement);
- Node furthestBlock;
+ Element furthestBlock;
for (var element in slice(tree.openElements, afeIndex)) {
if (specialElements.contains(getElementNameTuple(element))) {
furthestBlock = element;
@@ -2270,7 +2272,7 @@
}
class TextPhase extends Phase {
- TextPhase(parser) : super(parser);
+ TextPhase(HtmlParser parser) : super(parser);
// "Tried to process start tag %s in RCDATA/RAWTEXT mode"%token.name
@override
@@ -2321,7 +2323,7 @@
class InTablePhase extends Phase {
// http://www.whatwg.org/specs/web-apps/current-work///in-table
- InTablePhase(parser) : super(parser);
+ InTablePhase(HtmlParser parser) : super(parser);
@override
Token processStartTag(StartTagToken token) {
@@ -2622,7 +2624,7 @@
class InCaptionPhase extends Phase {
// http://www.whatwg.org/specs/web-apps/current-work///in-caption
- InCaptionPhase(parser) : super(parser);
+ InCaptionPhase(HtmlParser parser) : super(parser);
@override
Token processStartTag(StartTagToken token) {
@@ -2744,7 +2746,7 @@
class InColumnGroupPhase extends Phase {
// http://www.whatwg.org/specs/web-apps/current-work///in-column
- InColumnGroupPhase(parser) : super(parser);
+ InColumnGroupPhase(HtmlParser parser) : super(parser);
@override
Token processStartTag(StartTagToken token) {
@@ -2832,7 +2834,7 @@
class InTableBodyPhase extends Phase {
// http://www.whatwg.org/specs/web-apps/current-work///in-table0
- InTableBodyPhase(parser) : super(parser);
+ InTableBodyPhase(HtmlParser parser) : super(parser);
@override
Token processStartTag(StartTagToken token) {
@@ -2925,7 +2927,7 @@
return token;
}
- Token startTagTableOther(token) => endTagTable(token);
+ Token startTagTableOther(TagToken token) => endTagTable(token);
Token startTagOther(StartTagToken token) {
return parser._inTablePhase.processStartTag(token);
@@ -2971,7 +2973,7 @@
class InRowPhase extends Phase {
// http://www.whatwg.org/specs/web-apps/current-work///in-row
- InRowPhase(parser) : super(parser);
+ InRowPhase(HtmlParser parser) : super(parser);
@override
Token processStartTag(StartTagToken token) {
@@ -3117,7 +3119,7 @@
class InCellPhase extends Phase {
// http://www.whatwg.org/specs/web-apps/current-work///in-cell
- InCellPhase(parser) : super(parser);
+ InCellPhase(HtmlParser parser) : super(parser);
@override
Token processStartTag(StartTagToken token) {
@@ -3241,7 +3243,7 @@
}
class InSelectPhase extends Phase {
- InSelectPhase(parser) : super(parser);
+ InSelectPhase(HtmlParser parser) : super(parser);
@override
Token processStartTag(StartTagToken token) {
@@ -3397,7 +3399,7 @@
}
class InSelectInTablePhase extends Phase {
- InSelectInTablePhase(parser) : super(parser);
+ InSelectInTablePhase(HtmlParser parser) : super(parser);
@override
Token processStartTag(StartTagToken token) {
@@ -3523,7 +3525,7 @@
'var'
];
- InForeignContentPhase(parser) : super(parser);
+ InForeignContentPhase(HtmlParser parser) : super(parser);
void adjustSVGTagNames(token) {
final replacements = const {
@@ -3628,7 +3630,7 @@
if (asciiUpper2Lower(node.localName) == token.name) {
//XXX this isn't in the spec but it seems necessary
if (parser.phase == parser._inTableTextPhase) {
- InTableTextPhase inTableText = parser.phase;
+ final inTableText = parser.phase as InTableTextPhase;
inTableText.flushCharacters();
parser.phase = inTableText.originalPhase;
}
@@ -3653,7 +3655,7 @@
}
class AfterBodyPhase extends Phase {
- AfterBodyPhase(parser) : super(parser);
+ AfterBodyPhase(HtmlParser parser) : super(parser);
@override
Token processStartTag(StartTagToken token) {
@@ -3725,7 +3727,7 @@
class InFramesetPhase extends Phase {
// http://www.whatwg.org/specs/web-apps/current-work///in-frameset
- InFramesetPhase(parser) : super(parser);
+ InFramesetPhase(HtmlParser parser) : super(parser);
@override
Token processStartTag(StartTagToken token) {
@@ -3818,7 +3820,7 @@
class AfterFramesetPhase extends Phase {
// http://www.whatwg.org/specs/web-apps/current-work///after3
- AfterFramesetPhase(parser) : super(parser);
+ AfterFramesetPhase(HtmlParser parser) : super(parser);
@override
Token processStartTag(StartTagToken token) {
@@ -3875,7 +3877,7 @@
}
class AfterAfterBodyPhase extends Phase {
- AfterAfterBodyPhase(parser) : super(parser);
+ AfterAfterBodyPhase(HtmlParser parser) : super(parser);
@override
Token processStartTag(StartTagToken token) {
@@ -3926,7 +3928,7 @@
}
class AfterAfterFramesetPhase extends Phase {
- AfterAfterFramesetPhase(parser) : super(parser);
+ AfterAfterFramesetPhase(HtmlParser parser) : super(parser);
@override
Token processStartTag(StartTagToken token) {
diff --git a/lib/src/query_selector.dart b/lib/src/query_selector.dart
index 0c660b7..041c5d8 100644
--- a/lib/src/query_selector.dart
+++ b/lib/src/query_selector.dart
@@ -7,7 +7,7 @@
import 'package:html/dom.dart';
import 'package:html/src/constants.dart' show isWhitespaceCC;
-bool matches(Node node, String selector) =>
+bool matches(Element node, String selector) =>
SelectorEvaluator().matches(node, _parseSelectorList(selector));
Element querySelector(Node node, String selector) =>
@@ -40,10 +40,9 @@
}
Element querySelector(Node root, SelectorGroup selector) {
- for (var node in root.nodes) {
- if (node is! Element) continue;
- if (matches(node, selector)) return node;
- var result = querySelector(node, selector);
+ for (var element in root.nodes.whereType<Element>()) {
+ if (matches(element, selector)) return element;
+ var result = querySelector(element, selector);
if (result != null) return result;
}
return null;
@@ -51,10 +50,9 @@
void querySelectorAll(
Node root, SelectorGroup selector, List<Element> results) {
- for (var node in root.nodes) {
- if (node is! Element) continue;
- if (matches(node, selector)) results.add(node);
- querySelectorAll(node, selector, results);
+ for (var element in root.nodes.whereType<Element>()) {
+ if (matches(element, selector)) results.add(element);
+ querySelectorAll(element, selector, results);
}
}
@@ -71,13 +69,13 @@
int combinator;
for (var s in selector.simpleSelectorSequences.reversed) {
if (combinator == null) {
- result = s.simpleSelector.visit(this);
+ result = s.simpleSelector.visit(this) as bool;
} else if (combinator == TokenKind.COMBINATOR_DESCENDANT) {
// descendant combinator
// http://dev.w3.org/csswg/selectors-4/#descendant-combinators
do {
_element = _element.parent;
- } while (_element != null && !s.simpleSelector.visit(this));
+ } while (_element != null && !(s.simpleSelector.visit(this) as bool));
if (_element == null) result = false;
} else if (combinator == TokenKind.COMBINATOR_TILDE) {
@@ -85,7 +83,7 @@
// http://dev.w3.org/csswg/selectors-4/#general-sibling-combinators
do {
_element = _element.previousElementSibling;
- } while (_element != null && !s.simpleSelector.visit(this));
+ } while (_element != null && !(s.simpleSelector.visit(this) as bool));
if (_element == null) result = false;
}
@@ -216,10 +214,10 @@
// TODO(jmesserly): support An+B syntax too.
var exprs = selector.expression.expressions;
if (exprs.length == 1 && exprs[0] is LiteralTerm) {
- LiteralTerm literal = exprs[0];
+ final literal = exprs[0] as LiteralTerm;
var parent = _element.parentNode;
return parent != null &&
- literal.value > 0 &&
+ (literal.value as num) > 0 &&
parent.nodes.indexOf(_element) == literal.value;
}
break;
@@ -248,7 +246,7 @@
@override
bool visitNamespaceSelector(NamespaceSelector selector) {
// Match element tag name
- if (!selector.nameAsSimpleSelector.visit(this)) return false;
+ if (!(selector.nameAsSimpleSelector.visit(this) as bool)) return false;
if (selector.isNamespaceWildcard) return true;
@@ -273,7 +271,7 @@
// http://dev.w3.org/csswg/selectors-4/#negation
@override
bool visitNegationSelector(NegationSelector selector) =>
- !selector.negationArg.visit(this);
+ !(selector.negationArg.visit(this) as bool);
@override
bool visitAttributeSelector(AttributeSelector selector) {
diff --git a/lib/src/token.dart b/lib/src/token.dart
index e92ec8e..24c531e 100644
--- a/lib/src/token.dart
+++ b/lib/src/token.dart
@@ -63,9 +63,7 @@
return _string;
}
- StringToken(string)
- : _string = string,
- _buffer = string == null ? StringBuffer() : null;
+ StringToken(this._string) : _buffer = _string == null ? StringBuffer() : null;
StringToken add(String data) {
_buffer.write(data);
diff --git a/lib/src/tokenizer.dart b/lib/src/tokenizer.dart
index b26e8d9..26b7115 100644
--- a/lib/src/tokenizer.dart
+++ b/lib/src/tokenizer.dart
@@ -51,9 +51,7 @@
Token currentToken;
/// Holds a reference to the method to be invoked for the next parser state.
- // TODO(jmesserly): the type should be "Predicate" but a dart2js checked mode
- // bug prevents us from doing that. See http://dartbug.com/12465
- Function state;
+ bool Function() state;
final StringBuffer _buffer = StringBuffer();
@@ -79,9 +77,9 @@
reset();
}
- TagToken get currentTagToken => currentToken;
- DoctypeToken get currentDoctypeToken => currentToken;
- StringToken get currentStringToken => currentToken;
+ TagToken get currentTagToken => currentToken as TagToken;
+ DoctypeToken get currentDoctypeToken => currentToken as DoctypeToken;
+ StringToken get currentStringToken => currentToken as StringToken;
Token _current;
@override
diff --git a/lib/src/treebuilder.dart b/lib/src/treebuilder.dart
index e096214..b60d4a7 100644
--- a/lib/src/treebuilder.dart
+++ b/lib/src/treebuilder.dart
@@ -13,7 +13,7 @@
// The scope markers are inserted when entering object elements,
// marquees, table cells, and table captions, and are used to prevent formatting
// from "leaking" into tables, object elements, and marquees.
-const Node Marker = null;
+const Element Marker = null;
// TODO(jmesserly): this should extend ListBase<Element>, but my simple attempt
// didn't work.
@@ -230,7 +230,7 @@
return null;
}
- void insertRoot(Token token) {
+ void insertRoot(StartTagToken token) {
var element = createElement(token);
openElements.add(element);
document.nodes.add(element);
@@ -273,7 +273,7 @@
return element;
}
- Element insertElementTable(token) {
+ Element insertElementTable(StartTagToken token) {
/// Create an element and insert it into the tree
var element = createElement(token);
if (!tableInsertModeElements.contains(openElements.last.localName)) {
@@ -307,7 +307,7 @@
// We should be in the InTable mode. This means we want to do
// special magic element rearranging
var nodePos = getTableMisnestedNodePosition();
- _insertText(nodePos[0], data, span, nodePos[1]);
+ _insertText(nodePos[0], data, span, nodePos[1] as Element);
}
}
@@ -318,7 +318,7 @@
var nodes = parent.nodes;
if (refNode == null) {
if (nodes.isNotEmpty && nodes.last is Text) {
- Text last = nodes.last;
+ final last = nodes.last as Text;
last.appendData(data);
if (span != null) {
@@ -331,7 +331,7 @@
} else {
var index = nodes.indexOf(refNode);
if (index > 0 && nodes[index - 1] is Text) {
- Text last = nodes[index - 1];
+ final last = nodes[index - 1] as Text;
last.appendData(data);
} else {
nodes.insert(index, Text(data)..sourceSpan = span);
@@ -345,7 +345,7 @@
// The foster parent element is the one which comes before the most
// recently opened table element
// XXX - this is really inelegant
- Node lastTable;
+ Element lastTable;
Node fosterParent;
Node insertBefore;
for (var elm in openElements.reversed) {
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
index 57124c8..3eadc2e 100644
--- a/lib/src/utils.dart
+++ b/lib/src/utils.dart
@@ -79,7 +79,7 @@
result.write(padWithZeros(number, numberSize));
break;
case 'x':
- var number = value.toRadixString(16);
+ var number = (value as int).toRadixString(16);
result.write(padWithZeros(number, numberSize));
break;
default:
diff --git a/pubspec.yaml b/pubspec.yaml
index e7aa320..2576b05 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,8 +1,7 @@
name: html
-version: 0.14.0+4
+version: 0.15.0-dev
description: APIs for parsing and manipulating HTML content outside the browser.
-author: Dart Team <misc@dartlang.org>
homepage: https://github.com/dart-lang/html
environment:
diff --git a/test/parser_feature_test.dart b/test/parser_feature_test.dart
index 536f98f..3ed0031 100644
--- a/test/parser_feature_test.dart
+++ b/test/parser_feature_test.dart
@@ -13,7 +13,7 @@
_testElementSpans();
test('doctype is cloneable', () {
var doc = parse('<!doctype HTML>');
- DocumentType doctype = doc.nodes[0];
+ final doctype = doc.nodes[0] as DocumentType;
expect(doctype.clone(false).toString(), '<!DOCTYPE html>');
});
@@ -86,7 +86,7 @@
var textContent = '\n hello {{name}}';
var html = '<body><div>$textContent</div>';
var doc = parse(html, generateSpans: true);
- Text text = doc.body.nodes[0].nodes[0];
+ final text = doc.body.nodes[0].nodes[0] as Text;
expect(text, const TypeMatcher<Text>());
expect(text.data, textContent);
expect(text.sourceSpan.start.offset, html.indexOf(textContent));
@@ -186,35 +186,35 @@
});
test('escaping Text node in <script>', () {
- Element e = parseFragment('<script>a && b</script>').firstChild;
+ final e = parseFragment('<script>a && b</script>').firstChild as Element;
expect(e.outerHtml, '<script>a && b</script>');
});
test('escaping Text node in <span>', () {
- Element e = parseFragment('<span>a && b</span>').firstChild;
+ final e = parseFragment('<span>a && b</span>').firstChild as Element;
expect(e.outerHtml, '<span>a && b</span>');
});
test('Escaping attributes', () {
- Element e = parseFragment('<div class="a<b>">').firstChild;
+ var e = parseFragment('<div class="a<b>">').firstChild as Element;
expect(e.outerHtml, '<div class="a<b>"></div>');
- e = parseFragment('<div class=\'a"b\'>').firstChild;
+ e = parseFragment('<div class=\'a"b\'>').firstChild as Element;
expect(e.outerHtml, '<div class="a"b"></div>');
});
test('Escaping non-breaking space', () {
var text = '<span>foO\u00A0bar</span>';
expect(text.codeUnitAt(text.indexOf('O') + 1), 0xA0);
- Element e = parseFragment(text).firstChild;
+ var e = parseFragment(text).firstChild as Element;
expect(e.outerHtml, '<span>foO bar</span>');
});
test('Newline after <pre>', () {
- Element e = parseFragment('<pre>\n\nsome text</span>').firstChild;
+ var e = parseFragment('<pre>\n\nsome text</span>').firstChild as Element;
expect((e.firstChild as Text).data, '\nsome text');
expect(e.outerHtml, '<pre>\n\nsome text</pre>');
- e = parseFragment('<pre>\nsome text</span>').firstChild;
+ e = parseFragment('<pre>\nsome text</span>').firstChild as Element;
expect((e.firstChild as Text).data, 'some text');
expect(e.outerHtml, '<pre>some text</pre>');
});
@@ -278,7 +278,7 @@
test('Text.text', () {
var doc = parseFragment('<div>foo<div>bar</div>baz</div>');
var e = doc.firstChild;
- Text text = e.firstChild;
+ final text = e.firstChild as Text;
expect(text.data, 'foo');
expect(text.text, 'foo');
diff --git a/test/parser_test.dart b/test/parser_test.dart
index 21c54d2..232a50f 100644
--- a/test/parser_test.dart
+++ b/test/parser_test.dart
@@ -79,12 +79,10 @@
group(testName, () {
for (var testData in tests) {
var input = testData['data'];
- var errors = testData['errors'];
+ final errorString = testData['errors'];
+ final errors = errorString?.split('\n');
var innerHTML = testData['document-fragment'];
var expected = testData['document'];
- if (errors != null) {
- errors = errors.split('\n');
- }
for (var treeCtor in treeTypes.values) {
for (var namespaceHTMLElements in const [false, true]) {
diff --git a/test/selectors/level1_lib.dart b/test/selectors/level1_lib.dart
index f97dafc..721eca6 100644
--- a/test/selectors/level1_lib.dart
+++ b/test/selectors/level1_lib.dart
@@ -28,8 +28,7 @@
anyNS.id = 'any-namespace';
noNS.id = 'no-namespace';
- var div;
- div = [
+ var div = [
doc.createElement('div'),
doc.createElementNS('http://www.w3.org/1999/xhtml', 'div'),
doc.createElementNS('', 'div'),
@@ -70,7 +69,7 @@
/*
* Check that the querySelector and querySelectorAll methods exist on the given Node
*/
-void interfaceCheck(type, obj) {
+void interfaceCheck(String type, obj) {
runTest(() {
var q = obj.querySelector is Function;
assertTrue(q, type + ' supports querySelector.');
@@ -94,7 +93,7 @@
* Verify that the NodeList returned by querySelectorAll is static and and that a new list is created after
* each call. A static list should not be affected by subsequent changes to the DOM.
*/
-void verifyStaticList(type, root) {
+void verifyStaticList(String type, root) {
var pre, post, preLength;
runTest(() {
@@ -119,7 +118,7 @@
* Verify handling of special values for the selector parameter, including stringification of
* null and undefined, and the handling of the empty string.
*/
-void runSpecialSelectorTests(type, root) {
+void runSpecialSelectorTests(String type, root) {
// Dart note: changed these tests because we don't have auto conversion to
// String like JavaScript does.
runTest(() {
@@ -170,7 +169,7 @@
// 7
var result = root.querySelectorAll('*');
var i = 0;
- traverse(root, (elem) {
+ traverse(root as Node, (elem) {
if (!identical(elem, root)) {
assertEquals(
elem, result[i], 'The result in index $i should be in tree order.');
@@ -197,7 +196,7 @@
void runValidSelectorTest(String type, root,
List<Map<String, dynamic>> selectors, testType, docType) {
var nodeType = '';
- switch (root.nodeType) {
+ switch ((root as Node).nodeType) {
case Node.DOCUMENT_NODE:
nodeType = 'document';
break;
@@ -213,20 +212,21 @@
for (var i = 0; i < selectors.length; i++) {
var s = selectors[i];
- var n = s['name'];
+ var n = s['name'] as String;
var skip = _getSkip(n);
- var q = s['selector'];
- var e = s['expect'];
+ var q = s['selector'] as String;
+ var e = s['expect'] as List;
if ((s['exclude'] is! List ||
(s['exclude'].indexOf(nodeType) == -1 &&
s['exclude'].indexOf(docType) == -1)) &&
(s['testType'] & testType != 0)) {
//console.log("Running tests " + nodeType + ": " + s["testType"] + "&" + testType + "=" + (s["testType"] & testType) + ": " + JSON.stringify(s))
- var foundall, found;
+ List<Element> foundall;
+ Element found;
runTest(() {
- foundall = root.querySelectorAll(q);
+ foundall = root.querySelectorAll(q) as List<Element>;
assertNotEquals(foundall, null, 'The method should not return null.');
assertEquals(foundall.length, e.length,
'The method should return the expected number of matches.');
@@ -242,7 +242,7 @@
}, type + '.querySelectorAll: ' + n + ': ' + q, skip: skip);
runTest(() {
- found = root.querySelector(q);
+ found = root.querySelector(q) as Element;
if (e.isNotEmpty) {
assertNotEquals(found, null, 'The method should return a match.');
@@ -269,8 +269,8 @@
void runInvalidSelectorTest(String type, root, List selectors) {
for (var i = 0; i < selectors.length; i++) {
var s = selectors[i];
- var n = s['name'];
- var q = s['selector'];
+ var n = s['name'] as String;
+ var q = s['selector'] as String;
// Dart note: FormatException seems a reasonable mapping of SyntaxError
runTest(() {
@@ -298,7 +298,7 @@
}
}
-void runTest(Function body, String name, {String skip}) =>
+void runTest(dynamic Function() body, String name, {String skip}) =>
unittest.test(name, body, skip: skip);
void assertTrue(bool value, String reason) =>
diff --git a/test/support.dart b/test/support.dart
index afcf952..0199535 100644
--- a/test/support.dart
+++ b/test/support.dart
@@ -29,7 +29,7 @@
// TODO(jmesserly): make this class simpler. We could probably split on
// "\n#" instead of newline and remove a lot of code.
-class TestData extends IterableBase<Map> {
+class TestData extends IterableBase<Map<String, String>> {
final String _text;
final String newTestHeading;
@@ -40,12 +40,12 @@
// Note: in Python this was a generator, but since we can't do that in Dart,
// it's easier to convert it into an upfront computation.
@override
- Iterator<Map> get iterator => _getData().iterator;
+ Iterator<Map<String, String>> get iterator => _getData().iterator;
- List<Map> _getData() {
+ List<Map<String, String>> _getData() {
var data = <String, String>{};
String key;
- var result = <Map>[];
+ var result = <Map<String, String>>[];
var lines = _text.split('\n');
// Remove trailing newline to match Python
if (lines.last == '') {
@@ -79,7 +79,7 @@
return line.startsWith('#') ? line.substring(1).trim() : null;
}
- static Map normaliseOutput(Map data) {
+ static Map<String, String> normaliseOutput(Map<String, String> data) {
// Remove trailing newlines
data.forEach((key, value) {
if (value.endsWith('\n')) {
@@ -91,7 +91,7 @@
}
/// Serialize the [document] into the html5 test data format.
-String testSerializer(document) {
+String testSerializer(Node document) {
return (TestSerializer()..visit(document)).toString();
}
@@ -143,7 +143,7 @@
@override
void visitDocument(node) => _visitDocumentOrFragment(node);
- void _visitDocumentOrFragment(node) {
+ void _visitDocumentOrFragment(Node node) {
indent += 1;
for (var child in node.nodes) {
visit(child);
@@ -161,12 +161,16 @@
_str.write(node);
if (node.attributes.isNotEmpty) {
indent += 2;
- var keys = List.from(node.attributes.keys);
- keys.sort((x, y) => x.compareTo(y));
+ var keys = node.attributes.keys.toList();
+ keys.sort((x, y) {
+ if (x is String) return x.compareTo(y as String);
+ if (x is AttributeName) return x.compareTo(y as AttributeName);
+ throw StateError('Cannot sort');
+ });
for (var key in keys) {
var v = node.attributes[key];
if (key is AttributeName) {
- AttributeName attr = key;
+ final attr = key as AttributeName;
key = '${attr.prefix} ${attr.name}';
}
_newline();
diff --git a/test/tokenizer_test.dart b/test/tokenizer_test.dart
index 62d5f8a..41ae4d5 100644
--- a/test/tokenizer_test.dart
+++ b/test/tokenizer_test.dart
@@ -35,7 +35,8 @@
// Note: we can't get a closure of the state method. However, we can
// create a new closure to invoke it via mirrors.
var mtok = reflect(tokenizer);
- tokenizer.state = () => mtok.invoke(Symbol(_state), const []).reflectee;
+ tokenizer.state =
+ () => mtok.invoke(Symbol(_state), const []).reflectee as bool;
if (_lastStartTag != null) {
tokenizer.currentToken = StartTagToken(_lastStartTag);
@@ -45,25 +46,25 @@
var token = tokenizer.current;
switch (token.kind) {
case TokenKind.characters:
- processCharacters(token);
+ processCharacters(token as CharactersToken);
break;
case TokenKind.spaceCharacters:
- processSpaceCharacters(token);
+ processSpaceCharacters(token as SpaceCharactersToken);
break;
case TokenKind.startTag:
- processStartTag(token);
+ processStartTag(token as StartTagToken);
break;
case TokenKind.endTag:
- processEndTag(token);
+ processEndTag(token as EndTagToken);
break;
case TokenKind.comment:
- processComment(token);
+ processComment(token as CommentToken);
break;
case TokenKind.doctype:
- processDoctype(token);
+ processDoctype(token as DoctypeToken);
break;
case TokenKind.parseError:
- processParseError(token);
+ processParseError(token as ParseErrorToken);
break;
}
}
@@ -186,20 +187,22 @@
}
}
-void runTokenizerTest(Map testInfo) {
+void runTokenizerTest(Map<String, dynamic> testInfo) {
// XXX - move this out into the setup function
// concatenate all consecutive character tokens into a single token
if (testInfo.containsKey('doubleEscaped')) {
testInfo = unescape(testInfo);
}
- var expected = concatenateCharacterTokens(testInfo['output']);
+ var expected = concatenateCharacterTokens(testInfo['output'] as List);
if (!testInfo.containsKey('lastStartTag')) {
testInfo['lastStartTag'] = null;
}
- var parser = TokenizerTestParser(testInfo['initialState'],
- testInfo['lastStartTag'], testInfo['generateSpans'] ?? false);
- var tokens = parser.parse(testInfo['input']);
+ var parser = TokenizerTestParser(
+ testInfo['initialState'] as String,
+ testInfo['lastStartTag'] as String,
+ testInfo['generateSpans'] as bool /*?*/ ?? false);
+ var tokens = parser.parse(testInfo['input'] as String);
tokens = concatenateCharacterTokens(tokens);
var received = normalizeTokens(tokens);
var errorMsg = [
@@ -212,12 +215,12 @@
'\nreceived:',
tokens
].map((s) => '$s').join('\n');
- var ignoreErrorOrder = testInfo['ignoreErrorOrder'] ?? false;
+ var ignoreErrorOrder = testInfo['ignoreErrorOrder'] as bool /*?*/ ?? false;
expectTokensMatch(expected, received, ignoreErrorOrder, true, errorMsg);
}
-Map unescape(Map testInfo) {
+Map<String, dynamic> unescape(Map<String, dynamic> testInfo) {
// TODO(sigmundch,jmesserly): we currently use jsonDecode to unescape the
// unicode characters in the string, we should use a decoding that works with
// any control characters.
@@ -229,7 +232,7 @@
continue;
} else {
token[1] = decode(token[1]);
- if (token.length > 2) {
+ if ((token as List).length > 2) {
for (var pair in token[2]) {
var key = pair[0];
var value = pair[1];
@@ -260,17 +263,17 @@
var text = File(path).readAsStringSync();
var tests = jsonDecode(text);
var testName = pathos.basenameWithoutExtension(path);
- var testList = tests['tests'];
+ var testList = tests['tests'] as List;
if (testList == null) continue;
group(testName, () {
for (var index = 0; index < testList.length; index++) {
- final testInfo = testList[index];
+ final testInfo = testList[index] as Map<String, dynamic>;
testInfo.putIfAbsent('initialStates', () => ['Data state']);
for (var initialState in testInfo['initialStates']) {
test(testInfo['description'], () {
- testInfo['initialState'] = camelCase(initialState);
+ testInfo['initialState'] = camelCase(initialState as String);
runTokenizerTest(testInfo);
});
}