=encoding UTF-8 =head1 NAME Test2::Tools::DOM - Tools to test HTML/XML-based DOM representations =head1 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; =head1 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). =head1 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 L, 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 L for filtering the elements they will return. Please refer to L for additional details. =head2 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 L testing method. The input can either be a L object, or a string with the text representation of the DOM, which will be passed to the L 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. =head2 all_text all_text CHECK Takes a check only. Extracts the text content from all descendants of this element (by calling L<'all_text' on the Mojo::DOM58 object|Mojo::DOM58/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 }; =head2 at at SELECTOR, CHECK Takes a selector and a check. The selector is used to find the first matching descendant (by calling L<'at' on the Mojo::DOM58 object|Mojo::DOM58/at>), and this is passed to the provided check. The L 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 }; =head2 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 L<'attr' on the Mojo::DOM58 object|Mojo::DOM58/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 L 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 }; =head2 call call NAME, CHECK call [ NAME, ARGUMENTS ], CHECK call CODEREF, CHECK call_list ... call_hash ... I. Within the test context created by the L keyword the 'call' family of keywords provided by L can be used to make calls on the underlying L object as if the test were using the L. Please refer to the documentation in that distribution for more details on how to use this keyword. Since this is a core function from L, it will not be exported by this distribution. =head2 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 L<'children' on the Mojo::DOM58 object|Mojo::DOM58/children>). When called with a selector, only children that match will be passed to the check. is '

Text

  1. A
  2. B
', dom { children [ # First child is

dom { tag 'p' }, # Second child is

    dom { tag 'ol'; children li => [ dom { text 'A' }, dom { text 'B' }, ]; }, ]; }; =head2 content content CHECK Takes a check only. Extracts the raw content from this element and all its descendants (by calling L<'content' on the Mojo::DOM58 object|Mojo::DOM58/content>), and this is passed to the provided check. is '
    Hello, World!
    ', dom { content 'Hello, World!'; at em => dom { content 'World!' }; }; =head2 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 L<'find' on the Mojo::DOM58 object|Mojo::DOM58/find>). is '

    A

    B

    C

    ', dom { # Find all matching direct and indirect children find p => [ dom { text 'A' }, dom { text 'B' }, dom { text 'C' }, ]; }; =head2 tag tag CHECK Takes a check only. Extracts the tag of the current element (by calling L<'tag' on the Mojo::DOM58 object|Mojo::DOM58/tag>), and this is passed to the provided check. is '

    ', dom { tag 'p' }; =head2 text text CHECK Takes a check only. Extracts the text content from this element only (by calling L<'text' on the Mojo::DOM58 object|Mojo::DOM58/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 }; =head2 val val CHECK I. Takes a check only. Extracts the value from this element (by calling L<'val' on the Mojo::DOM58 object|Mojo::DOM58/val>), and this is passed to the provided check. is '', dom { val 42; attr value => 42; # The same, but longer }; =head1 SEE ALSO =over =item L A perfect companion to this distribution: Test2::Tools::HTTP supports the requests, Test2::Tools::DOM can be used to check the responses. =item L 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. =back =head1 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.