The Perl Toolchain Summit 2025 Needs You: You can help 🙏 Learn more

#!/usr/bin/perl -T
# This script contains a full test of the Node and EventTarget interfaces,
# since H:D:Attr implements them itself, and does not inherit
# from H:D:Node.
use strict; use warnings; use lib 't';
use Scalar::Util 'refaddr';
# -------------------------#
use tests 5; # constructors
my $doc = new HTML::DOM;
isa_ok $doc, 'HTML::DOM';
my $elem = $doc->createElement('a');
isa_ok $elem, 'HTML::DOM::Element';
$elem->setAttribute(href => 'about:blank');
my $attr = $elem->getAttributeNode('href');
isa_ok $attr, 'HTML::DOM::Attr';
ok $attr->DOES('HTML::DOM::Node'), '$attr does HTML::DOM::Node';
isa_ok $attr, 'HTML::DOM::EventTarget', '$attr';
# -------------------------#
use tests 1; # overloading
is $attr, 'about:blank', '""';
# -------------------------#
use tests 1; # name
is name $attr, 'href', 'name';
# -------------------------#
use tests 41; # specified
{
for(
[qw[ br clear none ]],
[qw[ td colspan 1 ]],
[qw[ th colspan 1 ]],
[qw[ form enctype application/x-www-form-urlencoded ]],
[qw[ frame frameborder 1 ]],
[qw[ iframe frameborder 1 ]],
[qw[ form method GET ]],
[qw[ td rowspan 1 ]],
[qw[ th rowspan 1 ]],
[qw[ frame scrolling auto ]],
[qw[ iframe scrolling auto ]],
[qw[ area shape rect ]],
[qw[ a shape rect ]],
[qw[ col span 1 ]],
[qw[ colgroup span 1 ]],
[qw[ input type TEXT ]],
[qw[ button type submit ]],
[qw[ param valuetype DATA ]],
) {
my $elem = $doc->createElement($$_[0]);
my $attr = $elem->getAttributeNode($$_[1]);
ok !$attr->specified, "@$_[0,1] !specified";
is $attr->value, $$_[2], "default value of @$_[0,1]";
}
my $doc = new HTML::DOM;
$doc->write('
<!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN">
<title></title>
');$doc->close;
ok !specified{$doc->documentElement->getAttributeNode('version')},
"html version !specified";
is $doc->documentElement->getAttributeNode('version')->value,
'-//W3C//DTD HTML 4.01 Transitional//EN',
"default value of html version";
my $elem = $doc->createElement('br');
ok specified{
removeAttributeNode$elem getAttributeNode$elem 'clear'
}, "specified after removal";
$elem->attr('clear','left');
ok specified{getAttributeNode$elem 'clear'},
'specified when explicit';
$elem->attr('clear','none');
ok specified{getAttributeNode$elem 'clear'},
'specified when explicit, even when eq default';
}
# -------------------------#
use tests 3; # value
is value $attr, 'about:blank', 'get value';
is $attr->value('javascript:window.close()'), 'about:blank',
'return value of setting the value is the old value';
is $elem->getAttribute('href'), 'javascript:window.close()',
'setting the value works';
# -------------------------#
use tests 15; # Node interface attributes
# HTML::DOM::Attr implements all the Node interface itself, and does not
# inherit from HTML::DOM::Node.
is nodeName $attr, 'href', 'nodeName';
is nodeValue $attr, 'javascript:window.close()', 'nodeValue';
cmp_ok nodeType $attr, '==', HTML::DOM::Node::ATTRIBUTE_NODE, 'nodeType';
is_deeply[parentNode$attr],[],'parentNode';
my $children = childNodes $attr;
my @children = childNodes $attr; # list context
is $children->length, 1, 'number of child nodes';
is scalar @children, 1, 'number of child nodes (list context)';
isa_ok my $text_node = $children->[0], 'HTML::DOM::Text', 'child node';
&cmp_ok(@children, '==', $text_node,
'(childNodes $attr)[0] is the same as childNodes $attr ->[0]');
is $text_node->data, 'javascript:window.close()',
'data contained by child node';
cmp_ok firstChild $attr, '==', $text_node, 'firstChild';
cmp_ok lastChild $attr, '==', $text_node, 'lastChild';
is_deeply[previousSibling$attr],[],'previousSibling';
is_deeply[ nextSibling$attr],[], 'nextSibling';
is_deeply[attributes$attr],[],'attributes';
cmp_ok ownerDocument $attr, '==', $doc, 'ownerDocument';
# -------------------------#
use tests 26; # Node interface methods
eval { insertBefore $attr };
isa_ok $@, 'HTML::DOM::Exception',
'$@ (after insertBefore)';
cmp_ok $@, '==',
HTML::DOM::Exception::NO_MODIFICATION_ALLOWED_ERR,
'insertBefore throws a "no modification allowed" error';
{ # replaceChild
$elem->appendChild(my $new_text =
createTextNode $doc 'http://www.perl.org/');
cmp_ok $text_node, '==', $attr->replaceChild(
$new_text, $text_node
), 'replaceChild returns the replaced node';
is scalar(()=childNodes$elem), 0,
'replaceChild removes from the tree first';
my $frag = createDocumentFragment $doc;
eval { $attr->replaceChild($frag, $new_text); };
isa_ok $@, 'HTML::DOM::Exception',
'$@ (after replaceChild with a frag that does not have ' .
'exactly one child node)';
cmp_ok $@, '==',
HTML::DOM::Exception::HIERARCHY_REQUEST_ERR,
'replaceChild with with a frag that does not have ' .
'exactly one child node throws a ' .
'hierarchy error';
$frag->appendChild(createTextNode $doc 'lalala');
$attr->replaceChild($frag, $new_text);
is $attr->value, 'lalala',
'replaceChild(frag,node) inserts the frag\'s children';
eval {
$attr->replaceChild(
(createAttribute $doc 'ddk'), $attr->firstChild
);
};
isa_ok $@, 'HTML::DOM::Exception',
'$@ (after replaceChild with wrong node type)';
cmp_ok $@, '==',
HTML::DOM::Exception::HIERARCHY_REQUEST_ERR,
'replaceChild with wrong node type throws a ' .
'hierarchy error';
appendChild $frag createElement $doc 'div';
eval { $attr->replaceChild($frag, firstChild $attr); };
isa_ok $@, 'HTML::DOM::Exception',
'$@ (after replaceChild with a frag containing the wrong' .
' node type)';
cmp_ok $@, '==',
HTML::DOM::Exception::HIERARCHY_REQUEST_ERR,
'replaceChild with a frag containing the wrong' .
' node type throws a ' .
'hierarchy error';
my $another_doc = new HTML::DOM;
my $another_node = createTextNode $another_doc 'ddk';
ok eval {
$attr->replaceChild(
$another_node,
(childNodes $attr)[0],
);
1
}, 'replaceChild with wrong doc no longer dies';
is $another_node->ownerDocument, $doc,
'replaceChild with wrong doc changes the owner doc';
eval {
$attr-> replaceChild(
$doc->createTextNode('ddk'), $text_node
);
};
isa_ok $@, 'HTML::DOM::Exception',
'$@ (after replaceChild with a bad refChild)';
cmp_ok $@, '==', HTML::DOM::Exception::NOT_FOUND_ERR,
'replaceChild with a 2nd arg that\'s not a child of ' .
'this node throws a "not found" error';
}
eval { removeChild $attr };
isa_ok $@, 'HTML::DOM::Exception',
'$@ (after removeChild)';
cmp_ok $@, '==',
HTML::DOM::Exception::NO_MODIFICATION_ALLOWED_ERR,
'removeChild throws a "no modification allowed" error';
eval { appendChild $attr };
isa_ok $@, 'HTML::DOM::Exception',
'$@ (after appendChild)';
cmp_ok $@, '==',
HTML::DOM::Exception::NO_MODIFICATION_ALLOWED_ERR,
'appendChild throws a "no modification allowed" error';
ok hasChildNodes $attr, 'hasChildNodes';
my $clone = cloneNode $attr; # shallow
cmp_ok refaddr $attr, '!=', refaddr $clone, 'cloneNode makes a new object';
cmp_ok +(childNodes $attr)[0], '!=', (childNodes $clone)[0],
'shallow clone works ignores its deep arg';
is_deeply [parentNode $clone], [], 'clones are orphans';
$clone = cloneNode $attr 1; # deep
cmp_ok refaddr $attr, '!=', refaddr $clone,
'deep cloneNode makes a new object';
cmp_ok +(childNodes $attr)[0], '!=', (childNodes $clone)[0],
'deep clone works';
is_deeply [parentNode $clone], [], 'deep clones are parentless';
# -------------------------#
use tests 4; # ownerElement
{
my $elem = $doc->createElement('a');
my $attr = $doc->createAttribute('href');
$elem->setAttributeNode($attr);
is $attr->ownerElement, $elem,
'ownerElement after setAttributeNode';
my $nother_attr = $doc->createAttribute('href');
$elem->setAttributeNode($nother_attr);
is +()=$attr->ownerElement, 0,
'ownerElement after setAttributeNode replaces it';
$elem->removeAttributeNode($nother_attr);
is $nother_attr->ownerElement, undef,
'removeAttributeNode updates ownerElement';
$elem->setAttribute('target', '_blank');
is $elem->getAttributeNode('target')->ownerElement, $elem,
'ownerElement of autovivified attr';
}
# -------------------------#
use tests 4; # XML namespace stuff and normal eyes
is +()=$attr->$_, 0, $_ for qw / namespaceURI prefix localName normalize /;
# -------------------------#
use tests 1; # hasAttributes
ok !$attr->hasAttributes, 'hasAttrbitues';
# -------------------------#
use tests 2; # booleanness
{
my $attr = $doc->createAttribute('foo');
ok !"$attr",
'make sure our booleanness test is actually doing something';
ok $attr, 'boooleannness';
}
# -------------------------#
use tests 2; # isSupported
ok $attr->isSupported('hTML', '1.0'), 'isSupported';
ok!$attr->isSupported('onfun') ,'isn’tSupported';
# -------------------------#
use tests 2; # relationship to the child text node (bug fixed in 0.017)
{
is refaddr $attr->firstChild->parentNode, refaddr $attr,
"an attr's text node's parent is the attr after replaceChild";
$attr = $doc->createAttribute('foo');
is refaddr $attr->firstChild->parentNode, refaddr $attr,
"a new attr's text node's parent is the attr";
}