Repository: aantron/lambdasoup (ocaml)

Issue:

Pull request:
Fix the usage of type selector in :has()
The current implementation of `:has()` selector does not exclude text nodes while selecting elements.

Subsequently, during the parsing, these text nodes end up being passed to function `name node`, which throws an error because it expects only element nodes.

As a result, currently a selector like `:has(ul)`, which contains a type selector as its argument, fails.

This PR fixes the aforementioned bug.

In the process, I also improved the assertion errors in the count tests, which now print, `expected: x but got: y`, instead of just `not equals`.

Patch:
diff --git a/src/soup.ml b/src/soup.ml
index c2e6b23..d7cb94e 100644
--- a/src/soup.ml
+++ b/src/soup.ml
@@ -598,6 +598,7 @@ struct
     | Content s -> texts node |> String.concat "" |> has_substring s
     | Has selector ->
       descendants node
+      |> filter (fun descendant -> not (is_text descendant))
       |> filter (fun descendant -> matches_simple_selector descendant selector)
       |> count
       |> fun count -> count > 0


Test patch:
diff --git a/test/test.ml b/test/test.ml
index d7c0ae0..8dedbb5 100644
--- a/test/test.ml
+++ b/test/test.ml
@@ -47,7 +47,7 @@ let suites = [
     ("parse-select-list" >:: fun _ ->
       let soup = page "list" |> parse in
       let test selector expected_count =
-        assert_equal ~msg:selector
+        assert_equal ~msg:selector ~printer:string_of_int
           (soup |> select selector |> count) expected_count
       in
 
@@ -122,11 +122,14 @@ let suites = [
       test "p:empty" 1;
       test "ul li:not(:nth-child(1))" 2;
       test ":not(ul) > li" 2;
-      test ":has([id=one])" 3;
+      test ":has(#one)" 3;
       test "ul:has([id=one])" 1;
-      test "ol:has([id=one])" 0;
+      test "ol:has(#one)" 0;
       test "li:has([id=one])" 0;
       test ":has(.odd)" 4;
+      test "ul:has(.even)" 1;
+      test ":has(ul)" 2;
+      test ":has(caption)" 0;
       test
         ("html:root > body.lists[class~=lists] > ul > li#one:nth-child(1) " ^
          "+ li#two")
@@ -135,7 +138,7 @@ let suites = [
     ("parse-select-quoted" >:: fun _ ->
       let soup = page "quoted" |> parse in
       let test selector expected_count =
-        assert_equal ~msg:selector
+        assert_equal ~msg:selector ~printer:string_of_int
           (soup |> select selector |> count) expected_count
       in
 
@@ -182,7 +185,7 @@ let suites = [
     ("parse-select-escaped" >:: fun _ ->
       let soup = page "quoted" |> parse in
       let test selector expected_count =
-        assert_equal ~msg:selector
+        assert_equal ~msg:selector ~printer:string_of_int
           (soup |> select selector |> count) expected_count
       in
 
@@ -203,7 +206,8 @@ let suites = [
     ("parse-select-html5" >:: fun _ ->
       let soup = page "html5" |> parse in
       let test selector expected_count =
-        assert_equal ~msg:selector (soup $$ selector |> count) expected_count
+        assert_equal ~msg:selector ~printer:string_of_int
+          (soup $$ selector |> count) expected_count
       in
 
       test "nav" 1;
@@ -215,7 +219,8 @@ let suites = [
       test "footer" 1);
 
     ("parse-select-google" >:: fun _ ->
-      assert_equal (page "google" |> parse $$ "form[action]" |> count) 1);
+      assert_equal ~printer:string_of_int
+        (page "google" |> parse $$ "form[action]" |> count) 1);
 
     ("parse-error" >:: fun _ ->
       let soup = parse "<p></p>" in
@@ -285,7 +290,7 @@ let suites = [
     ("generalized-select" >:: fun _ ->
       let soup = page "list" |> parse in
       let test root selector expected_count =
-        assert_equal ~msg:selector
+        assert_equal ~msg:selector ~printer:string_of_int
           (root |> select selector |> count) expected_count
       in
 
@@ -298,7 +303,8 @@ let suites = [
 
     ("select-attribute-operators" >:: fun _ ->
       let soup = "<form action=\"/continue\"></form>" |> parse in
-      assert_equal (soup $$ "form[action=/continue]" |> count) 1);
+      assert_equal ~printer:string_of_int
+        (soup $$ "form[action=/continue]" |> count) 1);
 
     ("select_one" >:: fun _ ->
       let soup = page "list" |> parse in
@@ -378,7 +384,8 @@ let suites = [
     ("matches-selector" >:: fun _ ->
       let soup = parse "<div> <p id='foo'>bar</p> </div>" in
       let elem = select_one "div p#foo" soup |> unwrap_option in
-      assert_bool "element matches selector" (matches_selector soup "div p#foo" elem)
+      assert_bool "element matches selector"
+        (matches_selector soup "div p#foo" elem)
     );
 
     ("fold_attributes" >:: fun _ ->