The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#============================================================= -*-perl-*-
#
# t/query.t
#
# Test script for the query() method.
#
# Written by Andy Wardley, October 2008
#
#========================================================================

use strict;
use warnings;
use lib qw( ./lib ../lib );
use HTML::TreeBuilder;
use Badger::Filesystem '$Bin Dir';
use Badger::Test
    tests => 57,
    debug => 'HTML::Query',
    args  => \@ARGV;

use HTML::Query 'Query';

our $Query    = 'HTML::Query';
our $Builder  = 'HTML::TreeBuilder';
our $test_dir = Dir($Bin);
our $html_dir = $test_dir->dir('html')->must_exist;
our $test1    = $html_dir->file('test1.html')->must_exist;

my ($query, $tree);


#-----------------------------------------------------------------------
# load up first test file and create an HTML::Query object for it.
#-----------------------------------------------------------------------

$tree = $Builder->new;
$tree->parse_file( $test1->absolute );
ok( $tree, 'parsed tree for first test file: ' . $test1->name );
$query = Query $tree;
ok( $query, 'created query' );

#-----------------------------------------------------------------------
# look for a specific element type: html
#-----------------------------------------------------------------------

my $html = $query->query('html');
ok( $html, 'got <html> query' );
is( $html->size, 1, 'one html element in query' );

#-----------------------------------------------------------------------
# look for a specific element type: p
#-----------------------------------------------------------------------

my $paras = $query->query('p');
ok( $paras, 'got <p> query' );
is( $paras->size, 2, 'two <p> elements in query' );
is( $paras->first->as_trimmed_text, 'paragraph one', 'got first para' );
is( $paras->last->as_trimmed_text, 'paragraph two', 'got last para' );
is( join(', ', $paras->as_trimmed_text), 'paragraph one, paragraph two',
    'got both paras as trimmed text' );


#-----------------------------------------------------------------------
# look for an element by id: #foo
#-----------------------------------------------------------------------

my $foo = $query->query('#foo');
ok( $foo, 'got #foo query' );
is( $foo->size, 1, 'one #foo element in query' );
is( $foo->first->as_trimmed_text, 'This is the foo div', 'got foo div' );


#-----------------------------------------------------------------------
# same again for specific element types: div#foo
#-----------------------------------------------------------------------

$foo = $query->query('div#foo');
ok( $foo, 'got div#foo query' );
is( $foo->size, 1, 'one div#foo element in query' );
is( $foo->first->as_trimmed_text, 'This is the foo div', 'got div#foo' );

my $none = $query->query('span#foo');
ok( $none, 'got span#foo query' );
is( $none->size, 0, 'no elements in query' );


#-----------------------------------------------------------------------
# look for specific elements by class: div.bar
#-----------------------------------------------------------------------

my $bars = $query->query('div.bar');
ok( $bars, 'got div.bar query' );
is( $bars->size, 2, 'two div.bar elements in query' );
is( $bars->first->as_trimmed_text, 'This is a div with bar class', 'got first div.bar' );
is( $bars->last->as_trimmed_text, 'This is another div with bar class', 'got last div.bar' );


#-----------------------------------------------------------------------
# look for all elements by class: .bar
#-----------------------------------------------------------------------

$bars = $query->query('.bar');
ok( $bars, 'got .bar query' );
is( $bars->size, 3, 'three .bar elements in query' );
is( $bars->first->as_trimmed_text, 'This is a div with bar class', 'got first .bar' );
is( $bars->last->as_trimmed_text, 'This is a span with bar class', 'got last .bar' );


#-----------------------------------------------------------------------
# look for all elements with a particular attribute value
#-----------------------------------------------------------------------

my $links = $query->query("a[href='index.html']");
ok( $links, "got a[href='index.html'] query" );
is( $links->size, 2, "two a[href='index.html'] elements in query" );
is( $links->first->as_trimmed_text, 'Link to home page', 'got first link to home page' );
is( $links->last->as_trimmed_text, 'Another link to home page', 'got second link to home page' );

# also check that works with different/no quoting
is( $query->query('a[href="index.html"]')->size, 2, 'got 2 with double quotes' );
is( $query->query('a[href=index.html]')->size, 2, 'got 2 with no quotes' );


#-----------------------------------------------------------------------
# look for elements with any value defined for an attribute
#-----------------------------------------------------------------------

$links = $query->query("a[rel]");
ok( $links, "got a[rel] query" );
is( $links->size, 3, "three a[rel] elements in query" );
is( join(', ', $links->as_trimmed_text), 'Forage, Nuts, Berries', 'got link with rel' );


#-----------------------------------------------------------------------
# multiple attributes
#-----------------------------------------------------------------------

my @inputs = $query->query('input.search[type=text][width=32]');
is( scalar(@inputs), 1, 'got one input with multiple attributes' );


#-----------------------------------------------------------------------
# multiple elements
#-----------------------------------------------------------------------

my $tds = $query->query('table tr.wibble td');
ok( $tds, 'got table tr.wibble td query' );
is( $tds->size, 2, 'two elements in table tr.wibble td query' );
is( join(', ', $tds->as_trimmed_text), 'Wibble1, Wibble2', 'got wibbles' );


#-----------------------------------------------------------------------
# list of specifications: table.foo, input.bar, etc
#-----------------------------------------------------------------------

$tds = $query->query('table.one tr.wibble td, table tr.wobble td, a[rel=nuts]');
ok( $tds, 'got comma sequence query' );
is( $tds->size, 4, 'four elements in composite query' );
is( join(', ', $tds->as_trimmed_text), 'Nuts, Wibble1, Wobble1, Wobble2', 'got wibbles, wobbles and nuts' );


#-----------------------------------------------------------------------
# check whitespace tolerance
#-----------------------------------------------------------------------

$tds = $query->query(
    'table.one tr.wibble td, 
    table 
    tr.wobble 
    td, 
    a[rel=nuts]'
);
is( $tds->size, 4, 'four elements in whitespace query' );


#-----------------------------------------------------------------------
# check we can call it in list context and get elements
#-----------------------------------------------------------------------

my @elems = $query->query('p');
is( scalar(@elems), 2, 'got two p elements in list context' );
is( ref $elems[0], 'HTML::Element', 'got HTML::Element object' );


#-----------------------------------------------------------------------
# check bad queries return errors
#-----------------------------------------------------------------------

ok( ! $query->try('query'), 'no query failed' );
is( $query->reason, 'html.query error - No query specified', 'got no query error message' );

ok( ! $query->try( query => '' ), 'empty query failed' );
is( $query->reason, 'html.query error - No query specified', 'got empty query error message' );

ok( ! $query->try( query => '  ' ), 'blank query failed' );
is( $query->reason, 'html.query error - Invalid query specified:   ', 'got blank query error message' );


#-----------------------------------------------------------------------
# check id/class with minus-character
#-----------------------------------------------------------------------

my @found = $query->query('#test-id');
is( scalar(@found), 1, 'found element with id "test-id"' );
is( $found[0]->as_trimmed_text, 'test-id content', 'got right element' );

@found = $query->query('.new-class');
is( scalar(@found), 4, 'found all elements with class "new-class"' );
is( $found[0]->as_trimmed_text, 'This is another div with bar class', 'got right 1st element' );
is( $found[1]->as_trimmed_text, 'This is a span with bar class', 'got right 2nd element' );
is( $found[2]->as_trimmed_text, 'Wobble1', 'got right 3rd element' );
is( $found[3]->as_trimmed_text, 'test-id content', 'got right 4th element' );