NAME
Test2::Tools::DOM - Tools to test HTML/XML-based DOM representations
SYNOPSIS
use Test2::V0;
use Test2::Tools::DOM;
my $html = <<'HTML';
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>A test document</title>
<link rel="icon" href="favicon.ico">
</head>
<body>
<p class="paragraph">Some text</p>
</body>
</html>
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
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, 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 for filtering the elements they will return.
Please refer to that distribution's documentation 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 testing method.
The input can either be a Mojo::DOM58 object, or a string with the text representation of the DOM, which will be passed to the Mojo::DOM58 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), and this is passed to the provided check.
is '<p>Hello, <em>World!</em></p>', 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), and this is passed to the provided check.
The Test2 existence checks can be used to check whether a given selector matches or not.
is '<div id=a><div id=b></div></div>', 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), 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 '<input type=checkbox name=answer value=42 checked>', 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 will be interpreted using XML-logic rather than Perl-logic: an attribute without a value in the DOM will be undefined but true.
is '<input type=checkbox name=answer value=42 checked>', 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
};
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).
When called with a selector, only children that match will be passed to the check.
is '<div><p>Text</p><ol><li>A</li><li>B</li></ol></div>', dom {
children [
# First child is <p>
dom { tag 'p' },
# Second child is <ol>
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), and this is passed to the provided check.
is '<div>Hello, <em>World!</em></div>', dom {
content 'Hello, <em>World!</em>';
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).
is '<div><p>A</p><div><p>B</p><div><p>C</p></div></div></div>', 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), and this is passed to the provided check.
is '<p></p>', 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), and this is passed to the provided check.
is '<p>Hello, <em>World!</em></p>', dom {
text 'Hello, '; # OK: 'World!' is not in this element
all_text 'Hello, World!'; # OK: use all_text for descendants' text
};
SEE ALSO
- Test2::Tools::HTTP
-
A perfect companion to this distribution: Test2::Tools::HTTP supports the requests, Test2::Tools::DOM can be used to check the responses.
- Test2::MojoX
-
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.