From 2b8962067d7fef9c2d86166b9b71491ed5f80699 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Sun, 30 Aug 2009 14:42:26 -0700 Subject: [PATCH] Removed leading "xpath_" from method names Also added support for multiple namespace prefixes, and tweaked the docs on namespaces. --- lib/Test/XPath.pm | 70 ++++++++++++++++++++++++++--------------------- t/simple.t | 40 +++++++++++++-------------- t/xpath.t | 38 ++++++++++++------------- 3 files changed, 78 insertions(+), 70 deletions(-) diff --git a/lib/Test/XPath.pm b/lib/Test/XPath.pm index 49538b7..6b096ff 100644 --- a/lib/Test/XPath.pm +++ b/lib/Test/XPath.pm @@ -10,14 +10,18 @@ sub new { my ($class, %p) = @_; my $doc = delete $p{doc} || _doc(\%p); my $xpc = XML::LibXML::XPathContext->new( $doc->documentElement ); - $xpc->registerNs( %{ $p{xmlns} }) if $p{xmlns}; + if (my $ns = $p{xmlns}) { + while (my ($k, $v) = each %{ $ns }) { + $xpc->registerNs( $k => $v ); + } + } return bless { xpc => $xpc, node => $doc->documentElement, }; } -sub xpath_ok { +sub ok { my ($self, $xpath, $code, $desc) = @_; my $xpc = $self->{xpc}; my $Test = Test::Builder->new; @@ -47,23 +51,23 @@ sub xpath_ok { sub node { shift->{node} } sub xpc { shift->{xpc} } -sub xpath_is { +sub is { Test::Builder::new->is_eq( _findv(shift, shift), @_); } -sub xpath_isnt { +sub isnt { Test::Builder::new->isnt_eq( _findv(shift, shift), @_); } -sub xpath_like { +sub like { Test::Builder::new->like( _findv(shift, shift), @_); } -sub xpath_unlike { +sub unlike { Test::Builder::new->unlike( _findv(shift, shift), @_); } -sub xpath_cmp_ok { +sub cmp_ok { Test::Builder::new->cmp_ok( _findv(shift, shift), @_); } @@ -132,19 +136,19 @@ Test::XPath - Test XML and HTML content and structure with XPath expressions no_network => 1, ); - $tx->xpath_ok( $xpath, $description ); - $tx->xpath_is( $xpath, $want, $description ); + $tx->ok( $xpath, $description ); + $tx->is( $xpath, $want, $description ); # Recursing into a document: my @css = qw(foo.css bar.css); - $tx->xpath_ok( '/html/head/style', sub { - shift->xpath_is( './@src', shift @css); + $tx->ok( '/html/head/style', sub { + shift->is( './@src', shift @css); }, $description); =head1 Description -Use the power of the XPath syntax supported by XML::LibXML to validate the -structure of your XML and HTML documents. +Use the power of XPath expressions to validate the structure of your XML and +HTML documents. =head2 Interface @@ -168,8 +172,8 @@ is passed. file => 'rss.xml', -Name of the file containing the XML to be parsed and tested. Required unless -the C or C option is passed. +Name of a file containing the XML to be parsed and tested. Required unless the +C or C option is passed. =item doc @@ -187,9 +191,13 @@ XML::LibXML's HTML parser will be used instead of the XML parser. =item xmlns - xmlns => { x => 'http://www.w3.org/1999/xhtml' }, + xmlns => { + x => 'http://www.w3.org/1999/xhtml', + a => 'http://www.w3.org/2007/app', + }, -Default XML namespace to be used in the XPath queries. +Set up prefixes for XML namespaces. Required if your XML uses namespaces and +you want to write reasonable XPath expressions. =item options @@ -201,33 +209,33 @@ L, such as =back -=head3 xpath_is +=head3 is - $xp->xpath_is('/html/head/title', 'Welcome'); + $xp->is('/html/head/title', 'Welcome'); -=head3 xpath_isnt +=head3 isnt - $xp->xpath_isnt('/html/head/link[@type]', 'hello'); + $xp->isnt('/html/head/link[@type]', 'hello'); -=head3 xpath_like +=head3 like - $xp->xpath_like('/html/head/title', qr/^Foobar Inc.: .+/); + $xp->like('/html/head/title', qr/^Foobar Inc.: .+/); -=head3 xpath_unlike +=head3 unlike - $xp->xpath_unlike() + $xp->unlike() -=head3 xpath_cmp_ok +=head3 cmp_ok - $xp->xpath_cmp_ok() + $xp->cmp_ok() -=head3 xpath_ok +=head3 ok - $xp->xpath_ok( '//foo/bar', 'Should have bar element under foo element' ); - $xp->xpath_ok( '//assets/story', sub { + $xp->ok( '//foo/bar', 'Should have bar element under foo element' ); + $xp->ok( '//assets/story', sub { my $i; for my $story (@_) { - $story->xpath_is('[@id]/text()', ++$i, "ID should be $i in story" ); + $story->is('[@id]/text()', ++$i, "ID should be $i in story" ); } }, 'Should have story elements' ); diff --git a/t/simple.t b/t/simple.t index 1cb3e31..9b96dda 100644 --- a/t/simple.t +++ b/t/simple.t @@ -13,16 +13,16 @@ ok my $xp = Test::XPath->new( is_html => 1, ), 'Create Test::XPath object'; -# Try successful xpath_ok. +# Try successful ok. test_out( 'ok 1 - whatever'); -$xp->xpath_ok('/html/head/title', 'whatever'); -test_test('xpath_ok works'); +$xp->ok('/html/head/title', 'whatever'); +test_test('ok works'); -# Try failed xpath_ok. +# Try failed ok. test_out('not ok 1 - whatever'); test_err(qq{# Failed test 'whatever'\n# at t/simple.t line 24.}); -$xp->xpath_ok('/html/head/foo', 'whatever'); -test_test('xpath_ok fail works'); +$xp->ok('/html/head/foo', 'whatever'); +test_test('ok fail works'); # Try a recursive call. test_out( 'ok 1 - p'); @@ -30,33 +30,33 @@ test_out( 'ok 2 - em'); test_out( 'ok 3 - b'); test_out( 'ok 4 - em'); test_out( 'ok 5 - b'); -$xp->xpath_ok( '/html/body/p', sub { - shift->xpath_ok('./em', sub { - $_->xpath_ok('./b', 'b'); +$xp->ok( '/html/body/p', sub { + shift->ok('./em', sub { + $_->ok('./b', 'b'); }, 'em'); }, 'p'); -test_test('recursive xpath_ok should work'); +test_test('recursive ok should work'); # Try is, like, and cmp_ok. -$xp->xpath_is( '/html/head/title', 'Hello', 'xpath_is should work'); -$xp->xpath_isnt( '/html/head/title', 'Bye', 'xpath_isnt should work'); -$xp->xpath_like( '/html/head/title', qr{^Hel{2}o$}, 'xpath_like should work'); -$xp->xpath_unlike( '/html/head/title', qr{^Bye$}, 'xpath_unlike should work'); -$xp->xpath_cmp_ok('/html/head/title', 'eq', 'Hello', 'xpath_cmp_ok should work'); +$xp->is( '/html/head/title', 'Hello', 'is should work'); +$xp->isnt( '/html/head/title', 'Bye', 'isnt should work'); +$xp->like( '/html/head/title', qr{^Hel{2}o$}, 'like should work'); +$xp->unlike( '/html/head/title', qr{^Bye$}, 'unlike should work'); +$xp->cmp_ok('/html/head/title', 'eq', 'Hello', 'cmp_ok should work'); # Try multiples. -$xp->xpath_is('/html/body/p', 'firstpost', 'Should work for multiples'); +$xp->is('/html/body/p', 'firstpost', 'Should work for multiples'); # Try an attribute. -$xp->xpath_is('/html/body/p/@class', 'foo', 'Should get attribute value'); +$xp->is('/html/body/p/@class', 'foo', 'Should get attribute value'); # Try a function. -$xp->xpath_is('count(/html/body/p)', 2, 'Should work for functions'); +$xp->is('count(/html/body/p)', 2, 'Should work for functions'); # Try a boolean function. -$xp->xpath_ok('boolean(1)', 'Boolean should work'); +$xp->ok('boolean(1)', 'Boolean should work'); # Try a false boolean. test_out('not ok 1 - false boolean'); -$xp->xpath_ok('false()', 'false boolean'); +$xp->ok('false()', 'false boolean'); test_test( skip_err => 1 ); diff --git a/t/xpath.t b/t/xpath.t index 807a4ed..092c1cf 100644 --- a/t/xpath.t +++ b/t/xpath.t @@ -33,51 +33,51 @@ isa_ok $xp, 'Test::XPath'; isa_ok $xp->{xpc}, 'XML::LibXML::XPathContext'; # Do some tests with it. -$xp->xpath_ok('/html/head/title', 'Should find the title'); +$xp->ok('/html/head/title', 'Should find the title'); # Try a recursive call. -$xp->xpath_ok( '/html/body/p', sub { - shift->xpath_ok('./em', sub { - $_->xpath_ok('./b', 'Find b under em'); +$xp->ok( '/html/body/p', sub { + shift->ok('./em', sub { + $_->ok('./b', 'Find b under em'); }, 'Find em under para'); }, 'Find paragraphs'); # Try is, like, and cmp_ok. -$xp->xpath_is( '/html/head/title', 'Hello', 'xpath_is should work'); -$xp->xpath_isnt( '/html/head/title', 'Bye', 'xpath_isnt should work'); -$xp->xpath_like( '/html/head/title', qr{^Hel{2}o$}, 'xpath_like should work'); -$xp->xpath_unlike( '/html/head/title', qr{^Bye$}, 'xpath_unlike should work'); -$xp->xpath_cmp_ok('/html/head/title', 'eq', 'Hello', 'xpath_cmp_ok should work'); +$xp->is( '/html/head/title', 'Hello', 'is should work'); +$xp->isnt( '/html/head/title', 'Bye', 'isnt should work'); +$xp->like( '/html/head/title', qr{^Hel{2}o$}, 'like should work'); +$xp->unlike( '/html/head/title', qr{^Bye$}, 'unlike should work'); +$xp->cmp_ok('/html/head/title', 'eq', 'Hello', 'cmp_ok should work'); # Try multiples. -$xp->xpath_is('/html/body/p', 'firstpost', 'Two values should concatenate'); +$xp->is('/html/body/p', 'firstpost', 'Two values should concatenate'); # Try loading a file. my $file = catfile qw(t menu.xml); ok $xp = Test::XPath->new( file => $file ), 'Should create with file'; # Do some tests on the XML. -$xp->xpath_is('/menu/restaurant', 'Trébol', 'Should find Unicode value in file'); +$xp->is('/menu/restaurant', 'Trébol', 'Should find Unicode value in file'); -# Use recursive xpath_ok() to ensure all items have the appropriate parts. +# Use recursive ok() to ensure all items have the appropriate parts. my $i = 0; -$xp->xpath_ok('/menu/item', sub { +$xp->ok('/menu/item', sub { ++$i; - $_->xpath_ok('./name', "Item $i should have a name"); - $_->xpath_ok('./price', "Item $i should have a price"); - $_->xpath_ok('./description', "Item $i should have a description"); + $_->ok('./name', "Item $i should have a name"); + $_->ok('./price', "Item $i should have a price"); + $_->ok('./description', "Item $i should have a description"); }, 'Should have items' ); # Hey, so no try using the doc param. ok $xp = Test::XPath->new( doc => XML::LibXML->new->parse_file($file), ), 'Should create with doc'; -$xp->xpath_is('/menu/restaurant', 'Trébol', 'Should find Unicode value in doc'); +$xp->is('/menu/restaurant', 'Trébol', 'Should find Unicode value in doc'); # Use a namespace. ok $xp = Test::XPath->new( xml => $xml, xmlns => { 'ex' => 'http://w3.org/ex' }, ), 'Should create with real namespace'; -$xp->xpath_ok('/ex:foo/ex:bar', 'We should find an ex:bar'); -$xp->xpath_is('/ex:foo/ex:bar[1]', 'first', 'Should be able to check the first ex:bar value'); +$xp->ok('/ex:foo/ex:bar', 'We should find an ex:bar'); +$xp->is('/ex:foo/ex:bar[1]', 'first', 'Should be able to check the first ex:bar value');