import 'package:test/test.dart';
import 'package:xml/xml.dart';
import 'utils/assertions.dart';
import 'utils/matchers.dart';
void main() {
test('basic', () {
final builder = XmlBuilder();
builder.declaration(encoding: 'UTF-8');
builder.processing(
'xml-stylesheet',
'href="/style.css" type="text/css" title="default stylesheet"',
);
builder.element(
'bookstore',
nest: () {
builder.comment('Only one book?');
builder.element(
'book',
nest: () {
builder.element(
'title',
nest: () {
builder.attribute('lang', 'en');
builder.text('Harry ');
builder.cdata('Potter');
},
);
builder.element('price', nest: 29.99);
},
);
},
);
final document = builder.buildDocument();
assertDocumentTreeInvariants(document);
final actual = document.toString();
const expected =
''
''
''
''
''
'Harry '
'29.99'
''
'';
expect(actual, expected);
});
test('all', () {
final builder = XmlBuilder();
builder.declaration();
builder.doctype('note', systemId: 'Note.dtd');
builder.processing('processing', 'instruction');
builder.element(
'element1',
attributes: {'attribute1': 'value1'},
nest: () {
builder.attribute(
'attribute2',
'value2',
attributeType: XmlAttributeType.DOUBLE_QUOTE,
);
builder.attribute(
'attribute3',
'value3',
attributeType: XmlAttributeType.SINGLE_QUOTE,
);
builder.element('element2');
builder.comment('comment');
builder.cdata('cdata');
builder.text('textual');
},
);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected =
''
''
''
''
''
''
''
'textual'
'';
expect(actual, expected);
});
test('self-closing', () {
final builder = XmlBuilder();
builder.element(
'element',
nest: () {
builder.element('self-closing-default');
builder.element('self-closing-true', isSelfClosing: true);
builder.element(
'self-closing-true-with-children',
isSelfClosing: true,
nest: '!',
);
builder.element('self-closing-false', isSelfClosing: false);
},
);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected =
''
''
''
'!'
''
'';
expect(actual, expected);
});
test('nested string', () {
final builder = XmlBuilder();
builder.element('element', nest: 'string');
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected = 'string';
expect(actual, expected);
});
test('nested iterable', () {
final builder = XmlBuilder();
builder.element(
'element',
nest: [
() => builder.text('st'),
'ri',
['n', 'g'],
],
);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected = 'string';
expect(actual, expected);
});
test('nested node (element)', () {
final builder = XmlBuilder();
final nested = XmlElement(XmlName('nested'));
builder.element('element', nest: nested);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
expect(xml.children[0].children[0].toXmlString(), nested.toXmlString());
expect(xml.children[0].children[0], isNot(same(nested)));
final actual = xml.toString();
const expected = '';
expect(actual, expected);
});
test('nested node (element, repeated)', () {
final builder = XmlBuilder();
final nested = XmlElement(XmlName('nested'));
builder.element('element', nest: [nested, nested]);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
expect(xml.children[0].children[0].toXmlString(), nested.toXmlString());
expect(xml.children[0].children[0], isNot(same(nested)));
expect(xml.children[0].children[1].toXmlString(), nested.toXmlString());
expect(xml.children[0].children[1], isNot(same(nested)));
final actual = xml.toString();
const expected = '';
expect(actual, expected);
});
test('nested node (text)', () {
final builder = XmlBuilder();
final nested = XmlText('text');
builder.element('element', nest: nested);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
expect(xml.children[0].children[0].toXmlString(), nested.toXmlString());
expect(xml.children[0].children[0], isNot(same(nested)));
final actual = xml.toString();
const expected = 'text';
expect(actual, expected);
});
test('nested node (text, repeated)', () {
final builder = XmlBuilder();
final nested = XmlText('text');
builder.element('element', nest: [nested, nested]);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
expect(xml.children[0].children[0].value, 'texttext');
expect(xml.children[0].children[0], isNot(same(nested)));
final actual = xml.toString();
const expected = 'texttext';
expect(actual, expected);
});
test('nested node (data)', () {
final builder = XmlBuilder();
final nested = XmlComment('abc');
builder.element('element', nest: nested);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
expect(xml.children[0].children[0].toXmlString(), nested.toXmlString());
expect(xml.children[0].children[0], isNot(same(nested)));
final actual = xml.toString();
const expected = '';
expect(actual, expected);
});
test('nested node (attribute)', () {
final builder = XmlBuilder();
final nested = XmlAttribute(XmlName('foo'), 'bar');
builder.element('element', nest: nested);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
expect(xml.children[0].attributes[0].toXmlString(), nested.toXmlString());
expect(xml.children[0].attributes[0], isNot(same(nested)));
final actual = xml.toString();
const expected = '';
expect(actual, expected);
});
test('nested node (document)', () {
final builder = XmlBuilder();
final nested = XmlDocument([]);
expect(() => builder.element('element', nest: nested), throwsArgumentError);
});
test('nested node (document fragment)', () {
final builder = XmlBuilder();
final nested = XmlDocumentFragment([XmlText('foo'), XmlComment('bar')]);
builder.element('element', nest: nested);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
expect(
xml.children[0].children[0].toXmlString(),
nested.children[0].toXmlString(),
);
expect(xml.children[0].children[0], isNot(same(nested.children[0])));
expect(
xml.children[0].children[1].toXmlString(),
nested.children[1].toXmlString(),
);
expect(xml.children[0].children[1], isNot(same(nested.children[1])));
final actual = xml.toString();
const expected = 'foo';
expect(actual, expected);
});
test('text', () {
final builder = XmlBuilder();
builder.element(
'text',
nest: () {
builder.text('abc');
builder.text('');
builder.text('def');
},
);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected = 'abcdef';
expect(actual, expected);
});
test('doctype (plain)', () {
final builder = XmlBuilder()
..doctype('note')
..element('root');
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected = '';
expect(actual, expected);
});
test('doctype (system ID)', () {
final builder = XmlBuilder()
..doctype('note', systemId: 'system.dtd')
..element('root');
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected = '';
expect(actual, expected);
});
test('doctype (public ID)', () {
final builder = XmlBuilder()
..doctype('note', publicId: 'public.dtd', systemId: 'system.dtd')
..element('root');
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected = '';
expect(actual, expected);
});
test('doctype (internal subset)', () {
final builder = XmlBuilder()
..doctype('note', internalSubset: '')
..element('root');
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected = ']>';
expect(actual, expected);
});
test('doctype (error)', () {
final builder = XmlBuilder();
expect(
() => builder.doctype('note', publicId: 'public.dtd'),
throwsArgumentError,
);
});
test('attribute', () {
final builder = XmlBuilder();
builder.element(
'element',
nest: () {
builder.attribute('foo', 'bar');
},
);
final xml = builder.buildDocument();
assertDocumentInvariants(xml);
final actual = xml.toString();
expect(actual, '');
});
test('attribute (single quote)', () {
final builder = XmlBuilder();
builder.element(
'element',
nest: () {
builder.attribute(
'foo',
'bar',
attributeType: XmlAttributeType.SINGLE_QUOTE,
);
},
);
final xml = builder.buildDocument();
assertDocumentInvariants(xml);
final actual = xml.toString();
expect(actual, '');
});
test('attribute (replaced)', () {
final builder = XmlBuilder();
builder.element(
'element',
nest: () {
builder.attribute('foo', 'bar');
builder.attribute('foo', 'zork');
},
);
final xml = builder.buildDocument();
assertDocumentInvariants(xml);
final actual = xml.toString();
expect(actual, '');
});
test('attribute (removed)', () {
final builder = XmlBuilder();
builder.element(
'element',
nest: () {
builder.attribute('foo', 'bar');
builder.attribute('foo', null);
},
);
final xml = builder.buildDocument();
assertDocumentInvariants(xml);
final actual = xml.toString();
expect(actual, '');
});
test('attribute (multiple)', () {
final builder = XmlBuilder();
builder.element(
'element',
nest: () {
for (var i = 1; i <= 5; i++) {
builder.attribute('a$i', i);
}
},
);
final xml = builder.buildDocument();
assertDocumentInvariants(xml);
final actual = xml.toString();
expect(actual, '');
});
test('attribute (update)', () {
final builder = XmlBuilder();
builder.element(
'element',
nest: () {
builder.attribute('lang', 'en');
builder.attribute('lang', 'de');
},
);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected = '';
expect(actual, expected);
});
test('attribute (update with namespace)', () {
const namespaces = {
'http://www.w3.org/1999/xhtml': 'xhtml',
'http://www.w3.org/1999/xlink': 'xlink',
};
final builder = XmlBuilder();
builder.element(
'element',
namespaces: namespaces,
nest: () {
for (final uri in namespaces.keys) {
builder.attribute('lang', 'en', namespace: uri);
}
for (final uri in namespaces.keys) {
builder.attribute('lang', 'de', namespace: uri);
}
},
);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected =
'';
expect(actual, expected);
});
test('attribute (update with unknown namespace)', () {
final builder = XmlBuilder();
builder.element(
'element',
nest: () {
builder.attribute('xhtml:lang', 'en');
builder.attribute('xhtml:lang', 'de');
},
);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected = '';
expect(actual, expected);
});
test('xml', () {
final builder = XmlBuilder();
builder.xml('');
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected = '';
expect(actual, expected);
});
test('xml (nested)', () {
final builder = XmlBuilder();
builder.element(
'outer',
nest: () {
builder.xml('');
},
);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected = '';
expect(actual, expected);
});
test('xml (multiple)', () {
final builder = XmlBuilder();
builder.element(
'outer',
nest: () {
builder.xml('');
builder.xml('hello');
},
);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected = 'hello';
expect(actual, expected);
});
test('xml (invalid)', () {
final builder = XmlBuilder();
builder.element(
'outer',
nest: () {
expect(
() => builder.xml(''),
throwsA(
isXmlTagException(
message: 'Missing ',
expectedName: 'broken',
actualName: isNull,
buffer: '',
position: 8,
),
),
);
},
);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected = '';
expect(actual, expected);
});
test('namespace binding', () {
const uri = 'http://www.w3.org/2001/XMLSchema';
final builder = XmlBuilder();
builder.element(
'schema',
nest: () {
builder.namespace(uri, 'xsd');
builder.attribute('lang', 'en', namespace: uri);
builder.element('element', namespace: uri);
},
namespace: uri,
);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected =
''
''
'';
expect(actual, expected);
});
test('default namespace binding', () {
const uri = 'http://www.w3.org/2001/XMLSchema';
final builder = XmlBuilder();
builder.element(
'schema',
nest: () {
builder.namespace(uri);
builder.attribute('lang', 'en', namespace: uri);
builder.element('element', namespace: uri);
},
namespace: uri,
);
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected =
''
''
'';
expect(actual, expected);
});
test('undefined namespace', () {
final builder = XmlBuilder();
expect(
() => builder.element('element', namespace: 'http://foo.com/'),
throwsArgumentError,
);
});
test('invalid namespace', () {
final builder = XmlBuilder();
builder.element(
'element',
nest: () {
expect(
() => builder.namespace('http://foo.com/', 'xml'),
throwsArgumentError,
);
expect(
() => builder.namespace('http://2.foo.com/', 'xmlns'),
throwsArgumentError,
);
},
);
final actual = builder.buildDocument().toString();
const expected = '';
expect(actual, expected);
});
test('conflicting namespace', () {
final builder = XmlBuilder();
builder.element(
'element',
nest: () {
builder.namespace('http://foo.com/', 'foo');
expect(
() => builder.namespace('http://2.foo.com/', 'foo'),
throwsArgumentError,
);
},
namespace: 'http://foo.com/',
);
final actual = builder.buildDocument().toString();
const expected = '';
expect(actual, expected);
});
test('unused namespace', () {
final builder = XmlBuilder();
builder.element(
'element',
nest: () {
builder.namespace('http://foo.com/', 'foo');
},
);
final actual = builder.buildDocument().toString();
const expected = '';
expect(actual, expected);
});
test('unused namespace (optimized)', () {
final builder = XmlBuilder(optimizeNamespaces: true);
builder.element(
'element',
nest: () {
builder.namespace('http://foo.com/', 'foo');
},
);
final actual = builder.buildDocument().toString();
const expected = '';
expect(actual, expected);
});
test('duplicate namespace', () {
final builder = XmlBuilder();
builder.element(
'element',
nest: () {
builder.namespace('http://foo.com/', 'foo');
builder.element(
'outer',
nest: () {
builder.namespace('http://foo.com/', 'foo');
builder.element(
'inner',
nest: () {
builder.namespace('http://foo.com/', 'foo');
builder.attribute('lang', 'en', namespace: 'http://foo.com/');
},
);
},
);
},
);
final actual = builder.buildDocument().toString();
const expected =
''
''
''
''
'';
expect(actual, expected);
});
test('duplicate namespace on attribute (optimized)', () {
final builder = XmlBuilder(optimizeNamespaces: true);
builder.element(
'element',
nest: () {
builder.namespace('http://foo.com/', 'foo');
builder.element(
'outer',
nest: () {
builder.namespace('http://foo.com/', 'foo');
builder.element(
'inner',
nest: () {
builder.namespace('http://foo.com/', 'foo');
builder.attribute('lang', 'en', namespace: 'http://foo.com/');
},
);
},
);
},
);
final actual = builder.buildDocument().toString();
const expected =
''
''
''
''
'';
expect(actual, expected);
});
test('duplicate namespace on element (optimized)', () {
final builder = XmlBuilder(optimizeNamespaces: true);
builder.element(
'element',
nest: () {
builder.namespace('http://foo.com/', 'foo');
builder.element(
'outer',
nest: () {
builder.namespace('http://foo.com/', 'foo');
builder.element('inner', namespace: 'http://foo.com/');
},
);
},
);
final actual = builder.buildDocument().toString();
const expected =
''
''
''
''
'';
expect(actual, expected);
});
test('namespace defined with element', () {
final builder = XmlBuilder();
builder.element(
'element',
namespaces: {'http://foo.com/': 'foo', 'http://bar.com/': null},
);
final actual = builder.buildDocument().toString();
const expected =
'';
expect(actual, expected);
});
test('entities cdata escape', () {
final builder = XmlBuilder();
builder.element('element', nest: '');
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected =
'<test><![CDATA[string]]></test>';
expect(actual, expected);
});
test('declaration', () {
final builder = XmlBuilder();
builder.declaration(
version: '0.5',
encoding: 'ASCII',
attributes: {'foo': 'bar'},
);
builder.element('data');
final xml = builder.buildDocument();
assertDocumentTreeInvariants(xml);
final actual = xml.toString();
const expected = '';
expect(actual, expected);
});
test('declaration outside of document', () {
final builder = XmlBuilder();
expect(
() => builder.element('data', nest: builder.declaration),
throwsA(
isXmlNodeTypeException(
message: startsWith('Got XmlNodeType.DECLARATION'),
node: isNotNull,
types: contains(XmlNodeType.ELEMENT),
),
),
);
});
test('exception during nesting', () {
final builder = XmlBuilder();
builder.element(
'outer',
nest: () {
expect(
() =>
builder.element('inner', nest: () => throw UnimplementedError()),
throwsUnsupportedError,
);
},
);
final document = builder.buildDocument();
expect(document.toString(), '');
});
test('incomplete builder', () {
final builder = XmlBuilder();
builder.element(
'element',
nest: () {
expect(builder.buildDocument, throwsStateError);
},
);
final document = builder.buildDocument();
expect(document.toString(), '');
});
test('reused builder', () {
final builder = XmlBuilder();
builder.element('element-one');
final firstDocument = builder.buildDocument();
expect(firstDocument.toString(), '');
builder.element('element-two');
final secondDocument = builder.buildDocument();
expect(secondDocument.toString(), '');
});
test('fragment builder', () {
final builder = XmlBuilder();
builder.element('element-one');
builder.element('element-two');
final xml = builder.buildFragment();
assertFragmentInvariants(xml);
});
}