# NAME
Test2::Tools::DOM - Tools to test HTML/XML-based DOM representations
# SYNOPSIS
use Test2::V0;
use Test2::Tools::DOM;
my $html = <<'HTML';
A test document
Some text
HTML
is $html, dom {
children bag {
item dom { tag 'body' };
item dom { tag 'head' };
end;
};
at 'link[rel=icon]' => dom {
attr href => 'favicon.ico'
};
find '.paragraph' => array {
item dom { text 'Some text' };
end;
};
};
done_testing;
# DESCRIPTION
Test2::Tools::DOM exports a set of testing functions designed to make it
easier to write declarative tests for XML-based DOM representations. This
will most commonly be HTML documents, but it can include other similar types
of documents (eg. SVG images, other XML documents, etc).
# FUNCTIONS
Unless otherwise stated, the functions described in this section are exported
by default by this distribution.
Most of the heavy lifting behind the scenes is done by [Mojo::DOM58](https://metacpan.org/pod/Mojo%3A%3ADOM58), and
most of the functions described below are thin wrappers around the methods
in that class with the same names.
Likewise, several of them support
[CSS selectors](https://metacpan.org/pod/Mojo::DOM58#SELECTORS)
for filtering the elements they will return.
Please refer to [that distribution's documentation](https://metacpan.org/pod/Mojo%3A%3ADOM58) for
additional details.
## dom
dom { ... }
Starts a new DOM testing context. It takes a single block, inside which the
rest of the functions described in this section can be used.
It can be used as the check in any [Test2](https://metacpan.org/pod/Test2) testing method.
The input can either be a [Mojo::DOM58](https://metacpan.org/pod/Mojo%3A%3ADOM58) object, or a string with the text
representation of the DOM, which will be passed to the [Mojo::DOM58](https://metacpan.org/pod/Mojo%3A%3ADOM58)
constructor.
For convenience, if the input is at the root node of the DOM tree, it will be
advanced to its first child element, if one exists.
## all\_text
all_text CHECK
Takes a check only. Extracts the text content from all descendants of this
element (by calling
['all\_text' on the Mojo::DOM58 object](https://metacpan.org/pod/Mojo%3A%3ADOM58#all_text)), and this is
passed to the provided check.
is '
Hello, World!
', dom {
all_text 'Hello, World!'; # OK: includes text in descendants
text 'Hello, '; # OK: use text for the text of this element only
};
## at
at SELECTOR, CHECK
Takes a selector and a check. The selector is used to find the first matching
descendant (by calling ['at' on the Mojo::DOM58 object](https://metacpan.org/pod/Mojo%3A%3ADOM58#at)), and
this is passed to the provided check.
The
[Test2 existence checks](https://metacpan.org/pod/Test2::Tools::Compare/QUICK-CHECKS)
can be used to check whether a given selector matches or not.
is '
', dom {
attr id => 'a'; # OK, we start at #a
at '#b' => dom {
attr id => 'b'; # OK, we've moved to #b
};
at '#c' => DNE; # OK, this element does not exist
# A missing element matches U, F, and DNE
# A present element matches D, T, and E
};
## attr
attr CHECK
attr NAME, CHECK
Takes either a single check, or the name of an attribute and a check.
When called without a name, all attributes are fetched and passed to the
check as a hashref (by calling
['attr' on the Mojo::DOM58 object](https://metacpan.org/pod/Mojo%3A%3ADOM58#attr)), and this is passed to the
provided check.
When called with a name, only the attribute with that name will be read
and passed to the check.
is '', dom {
# Get a hashref with all attributes
# Hashref is then checked using standard Perl logic
attr hash {
field type => 'checkbox';
field name => 'answer';
field value => 42;
field checked => E; # OK: the attribute exists
field checked => U; # OK: the attribute has no value
field checked => F; # OK: undefined is false in Perl-land
end;
};
};
When fetching a single value, the
[Test2 boolean and existence checks](https://metacpan.org/pod/Test2::Tools::Compare/QUICK-CHECKS)
will be interpreted using XML-logic rather than Perl-logic: an attribute
without a value in the DOM will be undefined but true.
is '', dom {
attr type => 'checkbox';
attr name => 'answer';
attr value => 42;
# When fetching individual attributes, checks use XML-logic
attr checked => E; # OK: the attribute exists
attr checked => U; # OK: the attribute has no value, so it's undefined
attr checked => T; # OK: the attribute is present, so it's true
};
## call
call NAME, CHECK
call [ NAME, ARGUMENTS ], CHECK
call CODEREF, CHECK
call_list ...
call_hash ...
_Supported from version 0.004003_.
Within the test context created by the ["dom"](#dom) keyword the 'call' family of
keywords provided by [Test2::Tools::Compare](https://metacpan.org/pod/Test2%3A%3ATools%3A%3ACompare) can be used to make calls on the
underlying [Mojo::DOM58](https://metacpan.org/pod/Mojo%3A%3ADOM58) object as if the test were using the
[object builder](https://metacpan.org/pod/Test2%3A%3ATools%3A%3ACompare#OBJECT-BUILDER).
Please refer to the documentation in that distribution for more details on
how to use this keyword.
Since this is a core function from [Test2::Suite](https://metacpan.org/pod/Test2%3A%3ASuite), it will not be exported by
this distribution.
## children
children CHECK
children SELECTOR, CHECK
Takes either a single check, or a selector and a check.
When called without a selector, all direct children of the current element
will be passed to the check as a possibly empty arrayref (by calling
['children' on the Mojo::DOM58 object](https://metacpan.org/pod/Mojo%3A%3ADOM58#children)).
When called with a selector, only children that match will be passed to the
check.
is '
Text
A
B
', dom {
children [
# First child is
dom { tag 'p' },
# Second child is
dom {
tag 'ol';
children li => [
dom { text 'A' },
dom { text 'B' },
];
},
];
};
## content
content CHECK
Takes a check only. Extracts the raw content from this element and all its
descendants (by calling
['content' on the Mojo::DOM58 object](https://metacpan.org/pod/Mojo%3A%3ADOM58#content)), and this is passed
to the provided check.
is '
Hello, World!
', dom {
content 'Hello, World!';
at em => dom { content 'World!' };
};
## find
find SELECTOR, CHECK
Takes a selector and a check. The selector will be used to find all the
matching descendants of this elements, which will be passed to the check as a
possibly empty arrayref (by calling
['find' on the Mojo::DOM58 object](https://metacpan.org/pod/Mojo%3A%3ADOM58#find)).
is '
A
B
C
', dom {
# Find all matching direct and indirect children
find p => [
dom { text 'A' },
dom { text 'B' },
dom { text 'C' },
];
};
## tag
tag CHECK
Takes a check only. Extracts the tag of the current element (by calling
['tag' on the Mojo::DOM58 object](https://metacpan.org/pod/Mojo%3A%3ADOM58#tag)), and this is passed to
the provided check.
is '', dom { tag 'p' };
## text
text CHECK
Takes a check only. Extracts the text content from this element only (by
calling ['text' on the Mojo::DOM58 object](https://metacpan.org/pod/Mojo%3A%3ADOM58#text)), and this is
passed to the provided check.
is '
Hello, World!
', dom {
text 'Hello, '; # OK: 'World!' is not in this element
all_text 'Hello, World!'; # OK: use all_text for descendants' text
};
## val
val CHECK
_Available from version 0.004_.
Takes a check only. Extracts the value from this element (by calling
['val' on the Mojo::DOM58 object](https://metacpan.org/pod/Mojo%3A%3ADOM58#val)), and this is
passed to the provided check.
is '', dom {
val 42;
attr value => 42; # The same, but longer
};
# SEE ALSO
- [Test2::Tools::HTTP](https://metacpan.org/pod/Test2%3A%3ATools%3A%3AHTTP)
A perfect companion to this distribution: Test2::Tools::HTTP supports the
requests, Test2::Tools::DOM can be used to check the responses.
- [Test2::MojoX](https://metacpan.org/pod/Test2%3A%3AMojoX)
If you are used to using Test::Mojo and are looking for a way to use it with
the Test2 suite, then this distribution might be the right one for your needs.
# COPYRIGHT AND LICENSE
Copyright 2022 José Joaquín Atria
This library is free software; you can redistribute it and/or modify it under
the Artistic License 2.0.