This is an automated email from the ASF dual-hosted git repository.

borinquenkid pushed a commit to branch chore/remove-jodd-wot
in repository https://gitbox.apache.org/repos/asf/grails-core.git


The following commit(s) were added to refs/heads/chore/remove-jodd-wot by this 
push:
     new 3b8b35feaf Replace string assertions with Jsoup DOM parsing per review 
discussion
3b8b35feaf is described below

commit 3b8b35feafd7d57471deb1d5594969cc1551a553
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Mon Jul 27 14:34:01 2026 -0500

    Replace string assertions with Jsoup DOM parsing per review discussion
    
    jdaugherty flagged the string-based assertions this PR introduced as
    fragile (exact-serialization matching instead of semantic checks), and
    Copilot independently raised the same concern per-assertion. jamesfredley
    proposed the trade-off directly: stay dependency-free with tightened
    (regex) assertions, or trade the removed jodd-wot for a real, modern
    parser (Jsoup). jdaugherty chose Jsoup explicitly, anticipating more
    HTML-structure-sensitive tests from planned refactoring.
    
    Reimplements the three DefaultFieldTemplateSpec assertions against
    org.jsoup instead of raw strings, restoring the exact semantic checks
    the original jodd-wot/Jerry version had (root div has the fieldcontain/
    error/required class tokens - order and other-attributes insensitive -
    label text/for-attribute, label immediately precedes the input, and the
    required-indicator span's text) rather than the newly-added brittle
    literal-markup comparisons.
    
    jsoup has no existing version management in this repo; added
    jsoupVersion to gradle.properties following the same pattern already
    used for javassistVersion/jnrPosixVersion, since it's a single-module,
    test-only dependency.
    
    Co-Authored-By: Claude Sonnet 5 <[email protected]>
---
 gradle.properties                                  |  1 +
 grails-fields/build.gradle                         |  1 +
 .../formfields/DefaultFieldTemplateSpec.groovy     | 34 ++++++++++++++--------
 3 files changed, 24 insertions(+), 12 deletions(-)

diff --git a/gradle.properties b/gradle.properties
index a9caae06f5..6fc832911b 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -40,6 +40,7 @@ greenmailVersion=2.1.2
 javassistVersion=3.30.2-GA
 jnrPosixVersion=3.1.20
 joptSimpleVersion=5.0.4
+jsoupVersion=1.22.1
 jspApiVersion=4.0.0
 openTest4jVersion=1.3.0
 picocliVersion=4.7.6
diff --git a/grails-fields/build.gradle b/grails-fields/build.gradle
index 070dc273fd..587a85b3d5 100644
--- a/grails-fields/build.gradle
+++ b/grails-fields/build.gradle
@@ -54,6 +54,7 @@ dependencies {
     testImplementation project(':grails-testing-support-datamapping')
     testImplementation project(':grails-testing-support-web')
     testImplementation "org.javassist:javassist:$javassistVersion"
+    testImplementation "org.jsoup:jsoup:$jsoupVersion"
 
     testRuntimeOnly 'org.objenesis:objenesis' // Required by Spock for mocking 
classes without default constructor
 }
diff --git 
a/grails-fields/src/test/groovy/grails/plugin/formfields/DefaultFieldTemplateSpec.groovy
 
b/grails-fields/src/test/groovy/grails/plugin/formfields/DefaultFieldTemplateSpec.groovy
index 3c205cfb78..c3ffd95262 100644
--- 
a/grails-fields/src/test/groovy/grails/plugin/formfields/DefaultFieldTemplateSpec.groovy
+++ 
b/grails-fields/src/test/groovy/grails/plugin/formfields/DefaultFieldTemplateSpec.groovy
@@ -18,6 +18,9 @@
  */
 package grails.plugin.formfields
 
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Element
+
 import grails.testing.web.taglib.TagLibUnitTest
 import spock.lang.Specification
 
@@ -47,25 +50,26 @@ class DefaultFieldTemplateSpec extends Specification 
implements TagLibUnitTest<F
 
     void "default rendering"() {
         when:
-        String output = tagLib.renderDefaultField(model).toString()
+        Element root = renderRoot()
 
         then:
-        output.contains('<div class="fieldcontain">')
+        root.hasClass('fieldcontain')
+
+        and:
+        Element label = root.selectFirst('label')
+        label.text() == 'label'
+        label.attr('for') == 'property'
 
         and:
-        output.contains('<label class="" for="property">label</label>')
-        output.indexOf('<label class="" for="property">label</label>') < 
output.indexOf('<input name="property">')
+        label.nextElementSibling().is('input[name=property]')
     }
 
     void "container marked as invalid"() {
         given:
         model.invalid = true
 
-        when:
-        String output = tagLib.renderDefaultField(model).toString()
-
-        then:
-        output.contains('<div class="fieldcontain error">')
+        expect:
+        renderRoot().hasClass('error')
     }
 
     void "container marked as required"() {
@@ -73,13 +77,19 @@ class DefaultFieldTemplateSpec extends Specification 
implements TagLibUnitTest<F
         model.required = true
 
         when:
-        String output = tagLib.renderDefaultField(model).toString()
+        Element root = renderRoot()
 
         then:
-        output.contains('<div class="fieldcontain required">')
+        root.hasClass('required')
 
         and:
-        output.contains('<span class="required-indicator">*</span>')
+        Element indicator = root.selectFirst('label .required-indicator')
+        indicator.text() == '*'
+    }
+
+    private Element renderRoot() {
+        String output = tagLib.renderDefaultField(model).toString()
+        Jsoup.parseBodyFragment(output).body().children().first()
     }
 
 }

Reply via email to