blob: 022da355d9ca8ae82e4c374174497ebdeb1660c0 [file] [log] [blame]
/// Support code for the tests in this directory.
library support;
import 'dart:collection';
import 'dart:io';
import 'package:path/path.dart' as p;
import 'package:html/src/treebuilder.dart';
import 'package:html/dom.dart';
import 'package:html/dom_parsing.dart';
typedef TreeBuilder TreeBuilderFactory(bool namespaceHTMLElements);
Map<String, TreeBuilderFactory> _treeTypes;
Map<String, TreeBuilderFactory> get treeTypes {
if (_treeTypes == null) {
// TODO(jmesserly): add DOM here once it's implemented
_treeTypes = {"simpletree": (useNs) => new TreeBuilder(useNs)};
}
return _treeTypes;
}
final testDir = p.join(p.dirname(p.fromUri(Platform.packageConfig)), 'test');
final testDataDir = p.join(testDir, 'data');
Iterable<String> getDataFiles(String subdirectory) {
var dir = new Directory(p.join(testDataDir, subdirectory));
return dir.listSync().where((f) => f is File).map((f) => f.path);
}
// 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> {
final String _text;
final String newTestHeading;
TestData(String filename, [this.newTestHeading = "data"])
// Note: can't use readAsLinesSync here because it splits on \r
: _text = new File(filename).readAsStringSync();
// 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.
Iterator<Map> get iterator => _getData().iterator;
List<Map> _getData() {
var data = <String, String>{};
String key;
var result = <Map>[];
var lines = _text.split('\n');
// Remove trailing newline to match Python
if (lines.last == '') {
lines.removeLast();
}
for (var line in lines) {
var heading = sectionHeading(line);
if (heading != null) {
if (data.isNotEmpty && heading == newTestHeading) {
// Remove trailing newline
data[key] = data[key].substring(0, data[key].length - 1);
result.add(normaliseOutput(data));
data = <String, String>{};
}
key = heading;
data[key] = "";
} else if (key != null) {
data[key] = '${data[key]}$line\n';
}
}
if (data.isNotEmpty) {
result.add(normaliseOutput(data));
}
return result;
}
/// If the current heading is a test section heading return the heading,
/// otherwise return null.
static String sectionHeading(String line) {
return line.startsWith("#") ? line.substring(1).trim() : null;
}
static Map normaliseOutput(Map data) {
// Remove trailing newlines
data.forEach((key, value) {
if (value.endsWith("\n")) {
data[key] = value.substring(0, value.length - 1);
}
});
return data;
}
}
/// Serialize the [document] into the html5 test data format.
testSerializer(document) {
return (new TestSerializer()..visit(document)).toString();
}
/// Serializes the DOM into test format. See [testSerializer].
class TestSerializer extends TreeVisitor {
final StringBuffer _str;
int _indent = 0;
String _spaces = '';
TestSerializer() : _str = new StringBuffer();
String toString() => _str.toString();
int get indent => _indent;
set indent(int value) {
if (_indent == value) return;
var arr = new List<int>(value);
for (int i = 0; i < value; i++) {
arr[i] = 32;
}
_spaces = new String.fromCharCodes(arr);
_indent = value;
}
void _newline() {
if (_str.length > 0) _str.write('\n');
_str.write('|$_spaces');
}
visitNodeFallback(Node node) {
_newline();
_str.write(node);
visitChildren(node);
}
visitChildren(Node node) {
indent += 2;
for (var child in node.nodes) visit(child);
indent -= 2;
}
visitDocument(node) => _visitDocumentOrFragment(node);
_visitDocumentOrFragment(node) {
indent += 1;
for (var child in node.nodes) visit(child);
indent -= 1;
}
visitDocumentFragment(DocumentFragment node) =>
_visitDocumentOrFragment(node);
visitElement(Element node) {
_newline();
_str.write(node);
if (node.attributes.isNotEmpty) {
indent += 2;
var keys = new List.from(node.attributes.keys);
keys.sort((x, y) => x.compareTo(y));
for (var key in keys) {
var v = node.attributes[key];
if (key is AttributeName) {
AttributeName attr = key;
key = "${attr.prefix} ${attr.name}";
}
_newline();
_str.write('$key="$v"');
}
indent -= 2;
}
visitChildren(node);
}
}