import 'package:meta/meta.dart'; import 'package:test/test.dart'; import 'package:xml/xml.dart'; import 'utils/assertions.dart'; import 'utils/matchers.dart'; @isTest void mutatingTest( String description, String before, void Function(T node) action, String after, ) { test(description, () { final document = XmlDocument.parse(before); final node = [ document, ].followedBy(document.descendants).whereType().first; action(node); document.normalize(); expect(document.toXmlString(), after, reason: 'should be modified'); assertDocumentTreeInvariants(document); }); } @isTest void throwingTest( String description, String before, void Function(T node) action, Matcher matcher, ) { test(description, () { final document = XmlDocument.parse(before); final node = [ document, ].followedBy(document.descendants).whereType().first; expect(() => action(node), matcher); expect(document.toXmlString(), before, reason: 'should not be modified'); assertDocumentTreeInvariants(document); }); } void main() { group('update', () { mutatingTest( 'element (attribute value)', '', (node) => node.value = 'update', '', ); mutatingTest( 'element (self-closing: false)', '', (node) => node.isSelfClosing = false, '', ); mutatingTest( 'element (self-closing: true)', '', (node) => node.isSelfClosing = true, '', ); mutatingTest( 'cdata (value)', '', (node) => node.value = 'update', '', ); mutatingTest( 'cdata (text)', '', // ignore: deprecated_member_use_from_same_package (node) => node.text = 'update', '', ); mutatingTest( 'comment (value)', '', (node) => node.value = 'update', '', ); mutatingTest( 'comment (text)', '', // ignore: deprecated_member_use_from_same_package (node) => node.text = 'update', '', ); mutatingTest( 'text (value)', 'text', (node) => node.value = 'update', 'update', ); mutatingTest( 'text (text)', 'text', // ignore: deprecated_member_use_from_same_package (node) => node.text = 'update', 'update', ); mutatingTest( 'processing (value)', '', (node) => node.value = 'update', '', ); mutatingTest( 'processing (text)', '', // ignore: deprecated_member_use_from_same_package (node) => node.text = 'update', '', ); mutatingTest( 'declaration (version)', '', (node) => node.version = '1.5', '', ); mutatingTest( 'declaration (encoding)', '', (node) => node.encoding = 'utf-8', '', ); mutatingTest( 'declaration (standalone)', '', (node) => node.standalone = false, '', ); }); group('add', () { mutatingTest( 'element (attributes)', '', (node) => node.attributes.add(XmlAttribute(XmlName('attr'), 'value')), '', ); mutatingTest( 'element (children)', '', (node) => node.children.add(XmlText('Hello World')), 'Hello World', ); mutatingTest( 'element (copy attribute)', '', (node) => node.children.first.attributes.add(node.attributes.first.copy()), '', ); mutatingTest( 'element (copy children)', '', (node) => node.children.add(node.children.first.copy()), '', ); mutatingTest( 'element (fragment children)', '', (node) { final fragment = XmlDocumentFragment([ XmlText('Hello'), XmlElement(XmlName('element2')), XmlComment('comment'), ]); node.children.add(fragment); }, 'Hello', ); mutatingTest( 'element (repeated fragment children)', '', (node) { final fragment = XmlDocumentFragment([XmlElement(XmlName('element2'))]); node.children ..add(fragment) ..add(fragment); }, '', ); final wrong = XmlAttribute(XmlName('invalid'), 'invalid'); throwingTest( 'element (attribute children)', '', (node) => node.children.add(wrong), throwsA( isXmlNodeTypeException( node: wrong, types: contains(XmlNodeType.ELEMENT), ), ), ); throwingTest( 'element (parent error)', '', (node) => node.children.add(node.firstChild!), throwsA(isXmlParentException()), ); }); group('addAll', () { mutatingTest( 'element (attributes)', '', (node) => node.attributes.addAll([XmlAttribute(XmlName('attr'), 'value')]), '', ); mutatingTest( 'element (children)', '', (node) => node.children.addAll([XmlText('Hello World')]), 'Hello World', ); mutatingTest( 'element (copy attribute)', '', (node) => node.children.first.attributes.addAll([node.attributes.first.copy()]), '', ); mutatingTest( 'element (copy children)', '', (node) => node.children.addAll([node.children.first.copy()]), '', ); mutatingTest( 'element (fragment children)', '', (node) { final fragment = XmlDocumentFragment([ XmlText('Hello'), XmlElement(XmlName('element2')), XmlComment('comment'), ]); node.children.addAll([fragment]); }, 'Hello', ); mutatingTest( 'element (repeated fragment children)', '', (node) { final fragment = XmlDocumentFragment([XmlElement(XmlName('element2'))]); node.children.addAll([fragment, fragment]); }, '', ); final wrong = XmlAttribute(XmlName('invalid'), 'invalid'); throwingTest( 'element (attribute children)', '', (node) => node.children.addAll([wrong]), throwsA( isXmlNodeTypeException( node: wrong, types: contains(XmlNodeType.ELEMENT), ), ), ); throwingTest( 'element (parent error)', '', (node) => node.children.addAll([node.firstChild!]), throwsA(isXmlParentException()), ); }); group('innerText', () { mutatingTest('empty with text', '', (node) { expect(node.innerText, ''); node.innerText = 'inner text'; expect(node.innerText, 'inner text'); }, 'inner text'); mutatingTest('empty with text (encoded)', '', (node) { expect(node.innerText, ''); node.innerText = ''; expect(node.innerText, ''); }, '<child>'); mutatingTest( 'multiple with text', 'multiple nodes', (node) { expect(node.innerText, 'multiple nodes'); node.innerText = 'replaced'; expect(node.innerText, 'replaced'); }, 'replaced', ); mutatingTest('text with empty', 'contents', ( node, ) { expect(node.innerText, 'contents'); node.innerText = ''; expect(node.children, isEmpty); expect(node.innerText, ''); }, ''); throwingTest( 'unsupported text node', 'contents', (node) { expect(node.firstChild, isA()); node.firstChild!.innerText = 'error'; }, throwsA( isXmlNodeTypeException( message: 'XmlNodeType.TEXT cannot have child nodes.', node: isA(), types: isEmpty, ), ), ); }); group('innerXml', () { mutatingTest( 'empty with multiple', '', (node) { expect(node.innerXml, ''); node.innerXml = ' and '; expect(node.innerXml, ' and '); }, ' and ', ); mutatingTest( 'multiple with empty', ' and ', (node) { expect(node.innerXml, ' and '); node.innerXml = ''; expect(node.children, isEmpty); expect(node.innerXml, ''); }, '', ); throwingTest( 'unsupported text node', 'contents', (node) { expect(node.firstChild, isA()); node.firstChild!.innerXml = 'error'; }, throwsA( isXmlNodeTypeException( message: 'XmlNodeType.TEXT cannot have child nodes.', node: isA(), types: isEmpty, ), ), ); }); group('outerXml', () { mutatingTest( 'single with other', '', (node) { expect(node.firstChild!.outerXml, ''); node.firstChild!.outerXml = ''; expect(node.firstChild!.outerXml, ''); }, '', ); mutatingTest( 'single with multiple', '', (node) { final child = node.firstChild!; expect(child.outerXml, ''); child.outerXml = ' and '; }, ' and ', ); mutatingTest( 'multiple with empty', ' and ', (node) { expect(node.children[1].outerXml, ' and '); node.children[1].outerXml = ''; expect(node.children.length, 2); }, '', ); }); group('insert', () { mutatingTest( 'element (attributes)', '', (node) => node.attributes.insert(1, XmlAttribute(XmlName('attr2'), 'value2')), '', ); mutatingTest( 'element (children)', 'Hello', (node) => node.children.insert(1, XmlText(' World')), 'Hello World', ); mutatingTest( 'element (copy attribute)', '', (node) => node.children.first.attributes.insert( 1, node.attributes.first.copy(), ), '', ); mutatingTest( 'element (copy children)', '', (node) => node.children.insert(1, node.children.first.copy()), '', ); mutatingTest( 'element (fragment children)', '', (node) { final fragment = XmlDocumentFragment([ XmlText('Hello'), XmlElement(XmlName('element3')), XmlComment('comment'), ]); node.children.insert(1, fragment); }, 'Hello', ); mutatingTest( 'element (repeated fragment children)', '', (node) { final fragment = XmlDocumentFragment([XmlElement(XmlName('element3'))]); node.children ..insert(0, fragment) ..insert(2, fragment); }, '', ); throwingTest( 'element (attribute range error)', '', (node) => node.attributes.insert(2, XmlAttribute(XmlName('attr2'), 'value2')), throwsRangeError, ); throwingTest( 'element (children range error)', 'Hello', (node) => node.children.insert(2, XmlText(' World')), throwsRangeError, ); final wrong = XmlAttribute(XmlName('invalid'), 'invalid'); throwingTest( 'element (attribute children)', '', (node) => node.children.insert(0, wrong), throwsA( isXmlNodeTypeException( node: wrong, types: contains(XmlNodeType.ELEMENT), ), ), ); throwingTest( 'element (parent error)', '', (node) => node.children.insert(0, node.firstChild!), throwsA(isXmlParentException()), ); }); group('insertAll', () { mutatingTest( 'element (attributes)', '', (node) => node.attributes.insertAll(1, [ XmlAttribute(XmlName('attr2'), 'value2'), ]), '', ); mutatingTest( 'element (children)', 'Hello', (node) => node.children.insertAll(1, [XmlText(' World')]), 'Hello World', ); mutatingTest( 'element (copy attribute)', '', (node) => node.children.first.attributes.insertAll(1, [ node.attributes.first.copy(), ]), '', ); mutatingTest( 'element (copy children)', '', (node) => node.children.insertAll(1, [node.children.first.copy()]), '', ); mutatingTest( 'element (fragment children)', '', (node) { final fragment = XmlDocumentFragment([ XmlText('Hello'), XmlElement(XmlName('element3')), XmlComment('comment'), ]); node.children.insertAll(1, [fragment]); }, 'Hello', ); mutatingTest( 'element (repeated fragment children)', '', (node) { final fragment = XmlDocumentFragment([XmlElement(XmlName('element3'))]); node.children.insertAll(0, [fragment, fragment]); }, '', ); throwingTest( 'element (attribute range error)', '', (node) => node.attributes.insertAll(2, [ XmlAttribute(XmlName('attr2'), 'value2'), ]), throwsRangeError, ); throwingTest( 'element (children range error)', 'Hello', (node) => node.children.insertAll(2, [XmlText(' World')]), throwsRangeError, ); final wrong = XmlAttribute(XmlName('invalid'), 'invalid'); throwingTest( 'element (attribute children)', '', (node) => node.children.insertAll(0, [wrong]), throwsA( isXmlNodeTypeException( node: wrong, types: contains(XmlNodeType.ELEMENT), ), ), ); throwingTest( 'element (parent error)', '', (node) => node.children.insertAll(0, [node.firstChild!]), throwsA(isXmlParentException()), ); }); group('[]=', () { mutatingTest( 'element (attributes)', '', (node) => node.attributes[0] = XmlAttribute(XmlName('attr2'), 'value2'), '', ); mutatingTest( 'element (children)', 'Hello World', (node) => node.children[0] = XmlText('Dart rocks'), 'Dart rocks', ); throwingTest( 'element (attribute range error)', '', (node) => node.attributes[2] = XmlAttribute(XmlName('attr2'), 'value2'), throwsRangeError, ); throwingTest( 'element (children range error)', 'Hello', (node) => node.children[2] = XmlText(' World'), throwsRangeError, ); final wrong = XmlAttribute(XmlName('invalid'), 'invalid'); throwingTest( 'element (attribute children)', '', (node) => node.children[0] = wrong, throwsA( isXmlNodeTypeException( node: wrong, types: contains(XmlNodeType.ELEMENT), ), ), ); throwingTest( 'element (parent error)', '', (node) => node.children[0] = node.firstChild!, throwsA(isXmlParentException()), ); }); group('remove', () { mutatingTest( 'attribute', '', (node) => node.attributes.first.remove(), '', ); mutatingTest( 'element', 'Hello World', (node) => node.children.first.remove(), '', ); mutatingTest( 'element (attributes)', '', (node) => node.attributes.remove(node.attributes.first), '', ); mutatingTest( 'element (children)', 'Hello World', (node) => node.children.remove(node.children.first), '', ); mutatingTest( 'element (attribute children)', 'Hello World', (node) { final wrong = XmlAttribute(XmlName('invalid'), 'invalid'); node.children.remove(wrong); }, 'Hello World', ); }); group('removeAt', () { mutatingTest( 'element (attributes)', '', (node) => node.attributes.removeAt(1), '', ); throwingTest( 'element (attribute range error)', '', (node) => node.attributes.removeAt(2), throwsRangeError, ); mutatingTest( 'element (children)', 'Hello World', (node) => node.children.removeAt(0), '', ); throwingTest( 'element (children range error)', 'Hello World', (node) => node.children.removeAt(2), throwsRangeError, ); }); group('removeWhere', () { mutatingTest( 'element (attributes)', '', (node) => node.attributes.removeWhere((node) => node.localName == 'attr2'), '', ); mutatingTest( 'element (children)', '', (node) => node.children.removeWhere( (node) => node is XmlElement && node.localName == 'element3', ), '', ); }); group('retainWhere', () { mutatingTest( 'element (attributes)', '', (node) => node.attributes.retainWhere((node) => node.localName == 'attr1'), '', ); mutatingTest( 'element (children)', '', (node) => node.children.retainWhere( (node) => node is XmlElement && node.localName == 'element2', ), '', ); }); group('clear', () { mutatingTest( 'element (attributes)', '', (node) => node.attributes.clear(), '', ); mutatingTest( 'element (children)', '', (node) => node.children.clear(), '', ); }); group('removeLast', () { mutatingTest( 'element (attributes)', '', (node) => node.attributes.removeLast(), '', ); throwingTest( 'element (attribute range error)', '', (node) => node.attributes.removeLast(), throwsRangeError, ); mutatingTest( 'element (children)', 'Hello World', (node) => node.children.removeLast(), '', ); throwingTest( 'element (children range error)', '', (node) => node.children.removeLast(), throwsRangeError, ); }); group('removeRange', () { mutatingTest( 'element (attributes)', '', (node) => node.attributes.removeRange(0, 1), '', ); throwingTest( 'element (attribute range error)', '', (node) => node.attributes.removeRange(0, 3), throwsRangeError, ); mutatingTest( 'element (children)', '', (node) => node.children.removeRange(1, 2), '', ); throwingTest( 'element (children range error)', '', (node) => node.children.removeRange(0, 3), throwsRangeError, ); }); group('setRange', () { mutatingTest( 'element (attributes)', '', (node) => node.attributes.setRange(0, 1, [ XmlAttribute(XmlName('attr3'), 'value3'), ]), '', ); throwingTest( 'element (attribute range error)', '', (node) => node.attributes.setRange(0, 3, [ XmlAttribute(XmlName('attr3'), 'value3'), XmlAttribute(XmlName('attr4'), 'value4'), XmlAttribute(XmlName('attr5'), 'value5'), ]), throwsRangeError, ); mutatingTest( 'element (children)', '', (node) => node.children.setRange(1, 2, [XmlElement(XmlName('element4'))]), '', ); throwingTest( 'element (children range error)', '', (node) => node.children.setRange(0, 3, [ XmlElement(XmlName('element4')), XmlElement(XmlName('element5')), XmlElement(XmlName('element6')), ]), throwsRangeError, ); }); group('replace', () { mutatingTest( 'element node with text', '', (node) => node.firstChild!.replace(XmlText('child')), 'child', ); mutatingTest( 'element text with node', 'child', (node) => node.firstChild!.replace(XmlElement(XmlName('child'))), '', ); mutatingTest( 'element attribute with attribute', '', (node) => node.attributes.first.replace( XmlAttribute(XmlName('attr2'), 'value2'), ), '', ); mutatingTest( 'element text with empty fragment', '', (node) => node.firstChild!.replace(XmlDocumentFragment()), '', ); mutatingTest( 'element text with one element fragment', '', (node) => node.firstChild!.replace(XmlDocumentFragment([XmlText('child')])), 'child', ); mutatingTest( 'element text with multiple element fragment', '', (node) => node.firstChild!.replace( XmlDocumentFragment([ XmlElement(XmlName('child1')), XmlElement(XmlName('child2')), ]), ), '', ); mutatingTest( 'element node with multiple element fragment', 'beforeafter', (node) => node.children[1].replace( XmlDocumentFragment([ XmlElement(XmlName('child1')), XmlElement(XmlName('child2')), ]), ), 'beforeafter', ); }); group('replaceRange', () { mutatingTest( 'element (attributes)', '', (node) => node.attributes.replaceRange(0, 1, [ XmlAttribute(XmlName('attr3'), 'value3'), ]), '', ); throwingTest( 'element (attribute range error)', '', (node) => node.attributes.replaceRange(0, 3, [ XmlAttribute(XmlName('attr3'), 'value3'), XmlAttribute(XmlName('attr4'), 'value4'), XmlAttribute(XmlName('attr5'), 'value5'), ]), throwsRangeError, ); mutatingTest( 'element (children)', '', (node) => node.children.replaceRange(1, 2, [XmlElement(XmlName('element4'))]), '', ); throwingTest( 'element (children range error)', '', (node) => node.children.replaceRange(0, 3, [ XmlElement(XmlName('element4')), XmlElement(XmlName('element5')), XmlElement(XmlName('element6')), ]), throwsRangeError, ); }); group('unsupported method', () { throwingTest( 'fillRange', '', (node) => node.children.fillRange(0, 1), throwsUnsupportedError, ); throwingTest( 'setAll', '', (node) => node.children.setAll(0, []), throwsUnsupportedError, ); throwingTest( 'length', '', (node) => node.children.length = 2, throwsUnsupportedError, ); }); }