Skip to content

Add unit tests for Grails layout capture and response buffering - #16640

Merged
jdaugherty merged 2 commits into
7.0.xfrom
test/gsp-layout-coverage-7.0.x
Oct 16, 2026
Merged

jdaugherty merged 2 commits into
7.0.xfrom
test/gsp-layout-coverage-7.0.x

Conversation

@jamesfredley

Copy link
Copy Markdown
Contributor

Summary

  • Adds specs for layout response wrappers, HTML page parsing, decorators, and layout tag capture.
  • The layout module was one of the lowest-covered areas in the Codecov report.

Test plan

  • ./gradlew :grails-layout:test for the new specs (42 tests, 0 failures)

Cover page wrappers, HTML parsing, decorators, and layout tag capture.

Assisted-by: sisyphus-grok:grok-4.7
Copilot AI balanced review requested due to automatic review settings October 13, 2026 21:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

3 open findings
What changed in this PR

Adds Spock specs to improve coverage of the Grails layout module, focusing on response buffering, HTML parsing, decorators, and layout tag capture.

Changes:

  • Added unit specs for layout taglibs (GrailsLayoutTagLib, RenderGrailsLayoutTagLib) covering capture and page property behavior.
  • Added unit specs for layout rendering/decorators and response wrappers/buffering behavior.
  • Added unit specs for the HTML page parser and GSPGrailsLayoutPage buffer/reset behavior.
File Description
grails-gsp/​grails-layout/​src/​test/​groovy/​org/​apache/​grails/​web/​layout/​RenderGrailsLayoutTagLibSpec.groovy New taglib unit tests for page properties and layout tag rendering.
grails-gsp/​grails-layout/​src/​test/​groovy/​org/​apache/​grails/​web/​layout/​LayoutDecoratorSpec.groovy New tests for decorator rendering behavior and cleanup semantics.
grails-gsp/​grails-layout/​src/​test/​groovy/​org/​apache/​grails/​web/​layout/​GrailsViewBufferingResponseSpec.groovy New tests validating view buffering behavior across writer/stream and error/redirect.
grails-gsp/​grails-layout/​src/​test/​groovy/​org/​apache/​grails/​web/​layout/​GrailsPageResponseWrapperSpec.groovy New tests for response wrapper buffering/parsing, pass-through, and dispatch delegation.
grails-gsp/​grails-layout/​src/​test/​groovy/​org/​apache/​grails/​web/​layout/​GrailsLayoutTagLibSpec.groovy New tests for capture tags storing head/body/title/meta/content into the layout page.
grails-gsp/​grails-layout/​src/​test/​groovy/​org/​apache/​grails/​web/​layout/​GrailsHTMLPageParserSpec.groovy New tests for parsing title/head/body/properties and Content interface behavior.
grails-gsp/​grails-layout/​src/​test/​groovy/​org/​apache/​grails/​web/​layout/​GSPGrailsLayoutPageSpec.groovy New tests for buffer semantics, title capture stripping, “used” flags, and reset.

🧠 Review effort: Lite


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +63 to +72

when:
decorator.render(content, model, request, response, request.servletContext)

then:
1 * view.render(model, request, response) >> { Map values, servletRequest, servletResponse ->
assert servletRequest.getAttribute(RequestConstants.PAGE).body == 'Body'
assert servletRequest.getAttribute(EmbeddedGrailsLayoutView.GSP_GRAILS_LAYOUT_PAGE) instanceof GSPGrailsLayoutPage
servletResponse.getWriter().write(values.greeting + ': Body')
}
request.setAttribute(RequestConstants.PAGE, page)
}

void 'pageProperty renders values default values and entire attributes'() {
Comment on lines +66 to +68
applyTemplate('<g:ifPageProperty name="page.theme" equals="light">No</g:ifPageProperty>') == ''
applyTemplate('<g:ifPageProperty name="missing">No</g:ifPageProperty>') == ''
applyTemplate('<g:ifPageProperty>No</g:ifPageProperty>') == ''
Capture the request attributes seen during layout rendering into variables
and assert on them in the then block, so a failure reports which condition
broke instead of surfacing as a bare assert inside the mock interaction.

Cover the title buffer path of GSPGrailsLayoutPage.writeHead through the
wrapTitleTag and captureTitle tags, the preprocessor's XML closing marker on
an empty capture tag, wrapTitleTag without a layout page, pageProperty
without its required name attribute, and sendError being skipped once the
target response is committed.

Rename the pageProperty feature and use neutral body text in the negative
ifPageProperty cases so the expected empty output reads as intended.
@jdaugherty
jdaugherty merged commit b5fe338 into 7.0.x Oct 16, 2026
2 of 52 checks passed
@testlens-app

testlens-app Bot commented Oct 16, 2026

Copy link
Copy Markdown

🔎 No tests executed 🔎

🏷️ Commit: 9f8c9b8
▶️ Tests: 0 executed
⚪️ Checks: 56/56 completed


Learn more about TestLens at testlens.app/docs.

@jdaugherty
jdaugherty deleted the test/gsp-layout-coverage-7.0.x branch October 16, 2026 15:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants