Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.grails.web.layout

import org.grails.buffer.StreamCharBuffer
import spock.lang.Specification

class GSPGrailsLayoutPageSpec extends Specification {

void 'empty page writes no content and is unused'() {
given:
def page = new GSPGrailsLayoutPage()
def writer = new StringWriter()

when:
page.writeHead(writer)
page.writeBody(writer)
page.writePage(writer)

then:
writer.toString() == ''
page.getHead() == null
page.getBody() == null
page.getPage() == null
page.getTitleBuffer() == null
page.getContentBuffer('page.sidebar') == null
!page.isUsed()
!page.isTitleCaptured()
}

void 'captured buffers expose head body and original page separately'() {
given:
def page = new GSPGrailsLayoutPage(renderingLayout)
def original = '<html><head>head</head><body>body</body></html>'
page.setPageBuffer(buffer(original))
page.setHeadBuffer(buffer('head'))
page.setBodyBuffer(buffer('body'))
def head = new StringWriter()
def body = new StringWriter()
def full = new StringWriter()

when:
page.writeHead(head)
page.writeBody(body)
page.writeOriginal(full)

then:
page.isUsed()
page.getHead() == head.toString()
head.toString() == 'head'
page.getBody() == body.toString()
body.toString() == 'body'
page.getPage() == full.toString()
full.toString() == original
page.originalLength() == original.length()

where:
renderingLayout << [false, true]
}

void 'body output falls back to the whole page when no body has been captured'() {
given:
def page = new GSPGrailsLayoutPage()
page.setPageBuffer(buffer('fragment without a body tag'))
def writer = new StringWriter()

when:
page.writeBody(writer)

then:
writer.toString() == 'fragment without a body tag'
!page.isUsed()
}

void 'captured title is removed from head output but its property is retained'() {
given:
def page = new GSPGrailsLayoutPage()
page.setHeadBuffer(buffer('<TITLE class="main">Title</TITLE><meta name="layout" content="main">'))
page.addProperty('title', 'Title')
page.setTitleCaptured(true)
def writer = new StringWriter()

when:
page.writeHead(writer)

then:
writer.toString() == '<meta name="layout" content="main">'
page.title == 'Title'
page.isTitleCaptured()
}

void 'content and object properties mark the page as used'() {
given:
def page = new GSPGrailsLayoutPage()
def sidebar = buffer('Sidebar')

when:
page.addProperty('page.count', Integer.valueOf(3))
page.setContentBuffer('sidebar', sidebar)

then:
page.isUsed()
page.getProperty('page.count') == '3'
page.getProperty('page.sidebar') == ''
page.getContentBuffer('page.sidebar').is(sidebar)
page.getContentBuffer('page.missing') == null
GSPGrailsLayoutPage.content2htmlPage(page).is(page)
}

void 'reset clears captured content and flags so a page can be reused'() {
given:
def page = new GSPGrailsLayoutPage()
page.setHeadBuffer(buffer('head'))
page.setBodyBuffer(buffer('body'))
page.setPageBuffer(buffer('page'))
page.setTitleBuffer(buffer('title'))
page.setTitleCaptured(true)
page.setContentBuffer('sidebar', buffer('sidebar'))

when:
page.reset()

then:
!page.isUsed()
!page.isTitleCaptured()
page.getHead() == null
page.getBody() == null
page.getPage() == null
page.getTitleBuffer() == null
page.getContentBuffer('page.sidebar') == null
}

private static StreamCharBuffer buffer(String text) {
def buffer = new StreamCharBuffer()
buffer.getWriter().write(text)
buffer
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.grails.web.layout

import com.opensymphony.sitemesh.Content
import spock.lang.Specification

class GrailsHTMLPageParserSpec extends Specification {

void 'HTML parser extracts title head body and layout properties'() {
given:
def html = '<html lang="en"><head><title>Title</title><meta name="layout" content="main"></head>' +
'<body class="content"><p>Hello</p></body></html>'

when:
def page = new GrailsHTMLPageParser().parse(html.toCharArray())

then:
page.title == 'Title'
page.head == '<meta name="layout" content="main">'
page.body == '<p>Hello</p>'
page.getProperty('lang') == 'en'
page.getProperty('body.class') == 'content'
page.getProperty('meta.layout') == 'main'
}

void 'titles inside XML do not replace the HTML title'() {
given:
def html = '<html><head><title>Page title</title></head>' +
'<body><xml><book><title>Book title</title></book></xml>Body</body></html>'

when:
def page = new GrailsHTMLPageParser().parse(html.toCharArray())

then:
page.title == 'Page title'
page.body.contains('<title>Book title</title>')
page.body.endsWith('Body')
}

void 'parsed content exposes original head and body through the public Content interface'() {
given:
def html = '<html><head><title>Title</title><meta name="layout" content="main"></head><body>Body</body></html>'
Content content = new GrailsHTMLPageParser().parseContent(html.toCharArray())
def original = new StringWriter()
def head = new StringWriter()
def body = new StringWriter()

when:
content.writeOriginal(original)
content.writeHead(head)
content.writeBody(body)
content.addProperty('page.extra', 'value')

then:
original.toString() == html
content.originalLength() == html.length()
head.toString() == '<meta name="layout" content="main">'
body.toString() == 'Body'
content.title == 'Title'
content.getProperty('page.extra') == 'value'
GSPGrailsLayoutPage.content2htmlPage(content).body == 'Body'
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.grails.web.layout

import grails.testing.web.taglib.TagLibUnitTest
import org.grails.gsp.compiler.GrailsLayoutPreprocessor
import org.grails.plugins.web.taglib.GrailsLayoutTagLib
import spock.lang.Specification

class GrailsLayoutTagLibSpec extends Specification implements TagLibUnitTest<GrailsLayoutTagLib> {

GSPGrailsLayoutPage page

void setup() {
page = new GSPGrailsLayoutPage()
request.setAttribute(EmbeddedGrailsLayoutView.GSP_GRAILS_LAYOUT_PAGE, page)
}

void 'capture tags render HTML and store head body and title on the page'() {
when:
def output = applyTemplate('<grailsLayout:captureHead><grailsLayout:captureTitle>Title</grailsLayout:captureTitle>' +
'<meta name="layout" content="main"></grailsLayout:captureHead>' +
'<grailsLayout:captureBody class="content">Body</grailsLayout:captureBody>')

then:
output == '<head><title>Title</title><meta name="layout" content="main"></head><body class="content">Body</body>'
page.title == 'Title'
page.isTitleCaptured()
page.head.contains('<meta name="layout" content="main">')
page.body == 'Body'
page.getProperty('body.class') == 'content'
page.isUsed()
}

void 'wrapped title is captured in its own buffer so the layout head can drop it without reparsing'() {
when:
def output = applyTemplate('<grailsLayout:captureHead><grailsLayout:wrapTitleTag>' +
'<grailsLayout:captureTitle>Title</grailsLayout:captureTitle></grailsLayout:wrapTitleTag>' +
'<meta name="layout" content="main"></grailsLayout:captureHead>')
def layoutHead = new StringWriter()
page.writeHead(layoutHead)

then:
output == '<head><title>Title</title><meta name="layout" content="main"></head>'
page.title == 'Title'
page.isTitleCaptured()
page.titleBuffer != null
layoutHead.toString() == '<meta name="layout" content="main">'
}

void 'empty capture tags honour the preprocessor XML closing marker'() {
when:
def output = applyTemplate('<grailsLayout:captureMeta name="viewport" content="width" ' +
GrailsLayoutPreprocessor.XML_CLOSING_FOR_EMPTY_TAG_ATTRIBUTE_NAME + '="/"/>')

then:
output == '<meta name="viewport" content="width"/>'
page.getProperty('meta.viewport') == 'width'
}

void 'meta capture records both original and lowercase property names'() {
when:
def output = applyTemplate('<grailsLayout:captureMeta name="Description" content="Details"/>' +
'<grailsLayout:captureMeta http-equiv="Content-Type" content="text/html"/>')

then:
output == '<meta name="Description" content="Details"><meta http-equiv="Content-Type" content="text/html">'
page.getProperty('meta.Description') == 'Details'
page.getProperty('meta.description') == 'Details'
page.getProperty('meta.http-equiv.Content-Type') == 'text/html'
page.getProperty('meta.http-equiv.content-type') == 'text/html'
}

void 'parameter and content capture populate the page without writing the captured component'() {
when:
def output = applyTemplate('<grailsLayout:parameter name="theme" value="dark"/>' +
'<grailsLayout:captureContent tag="sidebar">Sidebar</grailsLayout:captureContent>')

then:
output == ''
page.getProperty('page.theme') == 'dark'
page.getContentBuffer('page.sidebar').toString() == 'Sidebar'
}

void 'capture tags still render without a layout page'() {
given:
request.removeAttribute(EmbeddedGrailsLayoutView.GSP_GRAILS_LAYOUT_PAGE)

expect:
applyTemplate('<grailsLayout:captureTitle/><grailsLayout:captureBody>Body</grailsLayout:captureBody>') ==
'<title></title><body>Body</body>'
applyTemplate('<grailsLayout:wrapTitleTag><grailsLayout:captureTitle>Title</grailsLayout:captureTitle>' +
'</grailsLayout:wrapTitleTag>') == '<title>Title</title>'
}
}
Loading
Loading