summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/src/com/vaadin/ui/declarative/Design.java219
-rw-r--r--server/src/com/vaadin/ui/declarative/DesignContext.java12
-rw-r--r--server/tests/src/com/vaadin/tests/design/DesignTest.java123
-rw-r--r--server/tests/src/com/vaadin/tests/design/ParseAllSupportedComponentsTest.java7
-rw-r--r--server/tests/src/com/vaadin/tests/design/ParseLayoutTest.java24
-rw-r--r--server/tests/src/com/vaadin/tests/design/TestLocale.java36
-rw-r--r--server/tests/src/com/vaadin/tests/design/verticallayout-one-child.html3
-rw-r--r--server/tests/src/com/vaadin/tests/design/verticallayout-two-children.html4
8 files changed, 347 insertions, 81 deletions
diff --git a/server/src/com/vaadin/ui/declarative/Design.java b/server/src/com/vaadin/ui/declarative/Design.java
index 89e992181b..433705c632 100644
--- a/server/src/com/vaadin/ui/declarative/Design.java
+++ b/server/src/com/vaadin/ui/declarative/Design.java
@@ -19,11 +19,13 @@ import java.beans.IntrospectionException;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.Serializable;
import java.util.Collection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Document.OutputSettings.Syntax;
import org.jsoup.nodes.DocumentType;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
@@ -46,28 +48,21 @@ import com.vaadin.ui.declarative.DesignContext.ComponentCreationListener;
*/
public class Design implements Serializable {
/**
- * Constructs a component hierarchy from the design specified as an html
- * document. The component hierarchy must contain exactly one top-level
- * Component. The component should be located under <body>, but also invalid
- * html containing the hierarchy without <html>, <head> and <body> tags is
- * accepted.
- *
+ * Parses the given input stream into a jsoup document
+ *
* @param html
- * the html document describing the component design
- * @return the DesignContext created while traversing the tree. The
- * top-level component of the created component hierarchy can be
- * accessed using result.getRootComponent(), where result is the
- * object returned by this method.
+ * the stream containing the design
+ * @return the parsed jsoup document
* @throws IOException
*/
- public static DesignContext parse(InputStream html) {
- Document doc;
+ private static Document parse(InputStream html) {
try {
- doc = Jsoup.parse(html, "UTF-8", "", Parser.htmlParser());
+ Document doc = Jsoup.parse(html, "UTF-8", "", Parser.htmlParser());
+ return doc;
} catch (IOException e) {
throw new DesignException("The html document cannot be parsed.");
}
- return parse(doc, null);
+
}
/**
@@ -79,7 +74,7 @@ public class Design implements Serializable {
* some uninitialized instance fields. The fields will be automatically
* populated when parsing the design based on the component ids, local ids,
* and captions of the components in the design.
- *
+ *
* @param html
* the html document describing the component design
* @param rootInstance
@@ -91,34 +86,9 @@ public class Design implements Serializable {
* object returned by this method.
* @throws IOException
*/
- public static DesignContext parse(InputStream html, Component rootInstance) {
- Document doc;
- try {
- doc = Jsoup.parse(html, "UTF-8", "", Parser.htmlParser());
- } catch (IOException e) {
- throw new DesignException("The html document cannot be parsed.");
- }
- return parse(doc, rootInstance);
- }
-
- /**
- * Constructs a component hierarchy from the design specified as an html
- * document given as a string. The component hierarchy must contain exactly
- * one top-level Component. The component should be located under <body>,
- * but also invalid html containing the hierarchy without <html>, <head> and
- * <body> tags is accepted.
- *
- * @param html
- * the html document describing the component design
- * @return the DesignContext created while traversing the tree. The
- * top-level component of the created component hierarchy can be
- * accessed using result.getRootComponent(), where result is the
- * object returned by this method.
- * @throws IOException
- */
- public static DesignContext parse(String html) {
- Document doc = Jsoup.parse(html);
- return parse(doc, null);
+ private static DesignContext parse(InputStream html, Component rootInstance) {
+ Document doc = parse(html);
+ return designToComponentTree(doc, rootInstance);
}
/**
@@ -130,7 +100,7 @@ public class Design implements Serializable {
* component with some uninitialized instance fields. The fields will be
* automatically populated when parsing the design based on the component
* ids, local ids, and captions of the components in the design.
- *
+ *
* @param html
* the html document describing the component design
* @param rootInstance
@@ -142,17 +112,19 @@ public class Design implements Serializable {
* object returned by this method.
* @throws IOException
*/
- public static DesignContext parse(String html, Component rootInstance) {
+ private static DesignContext parse(String html, Component rootInstance) {
Document doc = Jsoup.parse(html);
- return parse(doc, rootInstance);
+ return designToComponentTree(doc, rootInstance);
}
/**
* Constructs a component hierarchy from the design specified as an html
- * tree. If componentRoot is not null, the component instances created
- * during synchronizing the design are assigned to its member fields based
- * on their id, localId, and caption
+ * tree.
*
+ * If a component root is given, the component instances created during
+ * synchronizing the design are assigned to its member fields based on their
+ * id, local id, and caption
+ *
* @param doc
* the html tree
* @param componentRoot
@@ -160,9 +132,10 @@ public class Design implements Serializable {
* type must match the type of the root element in the design.
* The member fields whose type is assignable from
* {@link Component} are set when parsing the component tree
- *
+ *
*/
- private static DesignContext parse(Document doc, Component componentRoot) {
+ private static DesignContext designToComponentTree(Document doc,
+ Component componentRoot) {
DesignContext designContext = new DesignContext(doc);
designContext.getPrefixes(doc);
// No special handling for a document without a body element - should be
@@ -210,7 +183,7 @@ public class Design implements Serializable {
// createChild creates the entire component hierarchy
componentRoot = designContext.createChild(element);
}
- designContext.setComponentRoot(componentRoot);
+ designContext.setRootComponent(componentRoot);
return designContext;
}
@@ -218,13 +191,13 @@ public class Design implements Serializable {
* Generates an html tree representation representing the component
* hierarchy having the given root. The hierarchy is stored under <body> in
* the tree. The generated tree corresponds to a valid html document.
- *
- *
+ *
+ *
* @param root
* the root of the component hierarchy
* @return an html tree representation of the component hierarchy
*/
- public static Document createHtml(DesignContext designContext) {
+ private static Document createHtml(DesignContext designContext) {
// Create the html tree skeleton.
Document doc = new Document("");
DocumentType docType = new DocumentType("html", "", "", "");
@@ -239,7 +212,7 @@ public class Design implements Serializable {
// Append the design under <body> in the html tree. createNode
// creates the entire component hierarchy rooted at the
// given root node.
- Component root = designContext.getComponentRoot();
+ Component root = designContext.getRootComponent();
Node rootNode = designContext.createNode(root);
body.appendChild(rootNode);
return doc;
@@ -248,14 +221,142 @@ public class Design implements Serializable {
/**
* Generates an html file corresponding to the component hierarchy with the
* given root.
- *
+ *
* @param writer
* @param root
* @throws IOException
*/
- public static void createHtml(BufferedWriter writer, DesignContext ctx)
+ private static void createHtml(BufferedWriter writer, DesignContext ctx)
throws IOException {
String docAsString = createHtml(ctx).toString();
writer.write(docAsString);
}
+
+ /**
+ * Loads a design from the given file name using the given root component.
+ * <p>
+ * Any {@link Component} type fields in the root component which are not
+ * assigned (i.e. are null) are mapped to corresponding components in the
+ * design. Matching is done based on field name in the component class and
+ * id/local id/caption in the design file.
+ * <p>
+ * The type of the root component must match the root element in the design
+ *
+ * @param filename
+ * The file name to load. Loaded from the same package as the
+ * root component
+ * @param rootComponent
+ * The root component of the layout.
+ * @return The design context used in the load operation
+ * @throws DesignException
+ * If the design could not be loaded
+ */
+ public static DesignContext read(String filename, Component rootComponent)
+ throws DesignException {
+ InputStream stream = rootComponent.getClass().getResourceAsStream(
+ filename);
+ if (stream == null) {
+ throw new DesignException("File " + filename
+ + " was not found in the package "
+ + rootComponent.getClass().getPackage().getName());
+ }
+ return read(stream, rootComponent);
+ }
+
+ /**
+ * Loads a design from the given stream using the given root component.
+ * <p>
+ * Any {@link Component} type fields in the root component which are not
+ * assigned (i.e. are null) are mapped to corresponding components in the
+ * design. Matching is done based on field name in the component class and
+ * id/local id/caption in the design file.
+ * <p>
+ * The type of the root component must match the root element in the design
+ *
+ * @param stream
+ * The stream to read the design from
+ * @param rootComponent
+ * The root component of the layout.
+ * @return The design context used in the load operation
+ * @throws DesignException
+ * If the design could not be loaded
+ */
+ public static DesignContext read(InputStream design, Component rootComponent) {
+ if (design == null) {
+ throw new DesignException("Stream cannot be null");
+ }
+ Document doc = parse(design);
+ DesignContext context = designToComponentTree(doc, rootComponent);
+
+ return context;
+ }
+
+ /**
+ * Loads a design from the given input stream
+ *
+ * @param design
+ * The input stream which contains the design
+ * @return The root component of the design
+ */
+ public static Component read(InputStream design) {
+ DesignContext context = read(design, null);
+ return context.getRootComponent();
+
+ }
+
+ /**
+ * Writes the given component tree in design format to the given output
+ * stream
+ *
+ * @param component
+ * the root component of the component tree to write
+ * @param outputStream
+ * the output stream to write the design to. The design is always
+ * written as UTF-8
+ * @throws IOException
+ */
+ public static void write(Component component, OutputStream outputStream)
+ throws IOException {
+ DesignContext dc = new DesignContext();
+ dc.setRootComponent(component);
+ write(dc, outputStream);
+ }
+
+ /**
+ * Writes the component, given in the design context, in design format to
+ * the given output stream. The design context is used for writing local ids
+ * and other information not available in the component tree.
+ *
+ * @param component
+ * the root component of the component tree to write
+ * @param outputStream
+ * the output stream to write the design to. The design is always
+ * written as UTF-8
+ * @throws IOException
+ * if writing fails
+ */
+ public static void write(DesignContext designContext,
+ OutputStream outputStream) throws IOException {
+ Document doc = createHtml(designContext);
+ write(doc, outputStream);
+ }
+
+ /**
+ * Writes the given jsoup document to the output stream (in UTF-8)
+ *
+ * @param doc
+ * the document to write
+ * @param outputStream
+ * the stream to write to
+ * @throws IOException
+ * if writing fails
+ */
+ private static void write(Document doc, OutputStream outputStream)
+ throws IOException {
+ doc.outputSettings().indentAmount(4);
+ doc.outputSettings().syntax(Syntax.html);
+ doc.outputSettings().prettyPrint(true);
+ outputStream.write(doc.html().getBytes());
+ }
+
}
diff --git a/server/src/com/vaadin/ui/declarative/DesignContext.java b/server/src/com/vaadin/ui/declarative/DesignContext.java
index 753a2d87ba..a3026fca18 100644
--- a/server/src/com/vaadin/ui/declarative/DesignContext.java
+++ b/server/src/com/vaadin/ui/declarative/DesignContext.java
@@ -46,7 +46,7 @@ public class DesignContext implements Serializable {
.synchronizedMap(new HashMap<Class<?>, Object>());
// The root component of the component hierarchy
- private Component componentRoot = null;
+ private Component rootComponent = null;
// Attribute names for global id and caption and the prefix name for a local
// id
public static final String ID_ATTRIBUTE = "id";
@@ -280,7 +280,7 @@ public class DesignContext implements Serializable {
* located under <head> in the html document.
*
*/
- public void getPrefixes(Document doc) {
+ protected void getPrefixes(Document doc) {
Element head = doc.head();
if (head == null) {
return;
@@ -554,15 +554,15 @@ public class DesignContext implements Serializable {
*
* @return
*/
- public Component getComponentRoot() {
- return componentRoot;
+ public Component getRootComponent() {
+ return rootComponent;
}
/**
* Sets the root component of a created component hierarchy.
*/
- public void setComponentRoot(Component componentRoot) {
- this.componentRoot = componentRoot;
+ public void setRootComponent(Component rootComponent) {
+ this.rootComponent = rootComponent;
}
/**
diff --git a/server/tests/src/com/vaadin/tests/design/DesignTest.java b/server/tests/src/com/vaadin/tests/design/DesignTest.java
new file mode 100644
index 0000000000..10bc2e0079
--- /dev/null
+++ b/server/tests/src/com/vaadin/tests/design/DesignTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed 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
+ *
+ * http://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 com.vaadin.tests.design;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Iterator;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.HasComponents;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.Panel;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.declarative.Design;
+import com.vaadin.ui.declarative.DesignContext;
+import com.vaadin.ui.declarative.DesignException;
+
+public class DesignTest {
+
+ @Test
+ public void readStream() throws FileNotFoundException {
+ Component root = Design
+ .read(new FileInputStream(
+ "server/tests/src/com/vaadin/tests/design/verticallayout-two-children.html"));
+ VerticalLayout rootLayout = (VerticalLayout) root;
+ Assert.assertEquals(VerticalLayout.class, root.getClass());
+
+ Assert.assertEquals(2, rootLayout.getComponentCount());
+ Assert.assertEquals(TextField.class, rootLayout.getComponent(0)
+ .getClass());
+ Assert.assertEquals(Button.class, rootLayout.getComponent(1).getClass());
+ }
+
+ @Test(expected = DesignException.class)
+ @Ignore("Feature needs to be fixed")
+ public void readWithIncorrectRoot() throws FileNotFoundException {
+ Design.read(
+ new FileInputStream(
+ "server/tests/src/com/vaadin/tests/design/verticallayout-one-child.html"),
+ new Panel());
+ }
+
+ public static class MyVerticalLayout extends VerticalLayout {
+
+ }
+
+ @Test
+ public void readWithSubClassRoot() throws FileNotFoundException {
+ Design.read(
+ new FileInputStream(
+ "server/tests/src/com/vaadin/tests/design/verticallayout-one-child.html"),
+ new MyVerticalLayout());
+ }
+
+ @Test
+ public void writeComponentToStream() throws IOException {
+ HorizontalLayout root = new HorizontalLayout(new Button("OK"),
+ new Button("Cancel"));
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ Design.write(root, baos);
+ Component newRoot = Design.read(new ByteArrayInputStream(baos
+ .toByteArray()));
+
+ assertHierarchyEquals(root, newRoot);
+ }
+
+ @Test
+ public void writeDesignContextToStream() throws IOException {
+ DesignContext dc = Design
+ .read(new FileInputStream(
+ "server/tests/src/com/vaadin/tests/design/verticallayout-two-children.html"),
+ null);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ Design.write(dc, baos);
+ Component newRoot = Design.read(new ByteArrayInputStream(baos
+ .toByteArray()));
+
+ assertHierarchyEquals(dc.getRootComponent(), newRoot);
+ }
+
+ private void assertHierarchyEquals(Component expected, Component actual) {
+ if (expected.getClass() != actual.getClass()) {
+ throw new AssertionError(
+ "Component classes do not match. Expected: "
+ + expected.getClass().getName() + ", was: "
+ + actual.getClass().getName());
+ }
+
+ if (expected instanceof HasComponents) {
+ HasComponents expectedHC = (HasComponents) expected;
+ HasComponents actualHC = (HasComponents) actual;
+ Iterator<Component> eI = expectedHC.iterator();
+ Iterator<Component> aI = actualHC.iterator();
+
+ while (eI.hasNext()) {
+ assertHierarchyEquals(eI.next(), aI.next());
+ }
+ }
+ }
+}
diff --git a/server/tests/src/com/vaadin/tests/design/ParseAllSupportedComponentsTest.java b/server/tests/src/com/vaadin/tests/design/ParseAllSupportedComponentsTest.java
index ff13522b2f..4f3f205631 100644
--- a/server/tests/src/com/vaadin/tests/design/ParseAllSupportedComponentsTest.java
+++ b/server/tests/src/com/vaadin/tests/design/ParseAllSupportedComponentsTest.java
@@ -33,11 +33,12 @@ public class ParseAllSupportedComponentsTest extends TestCase {
public void testParsing() {
try {
DesignContext ctx = Design
- .parse(new FileInputStream(
- "server/tests/src/com/vaadin/tests/design/all-components.html"));
+ .read(new FileInputStream(
+ "server/tests/src/com/vaadin/tests/design/all-components.html"),
+ null);
assertNotNull("The returned design context can not be null", ctx);
assertNotNull("the component root can not be null",
- ctx.getComponentRoot());
+ ctx.getRootComponent());
} catch (FileNotFoundException e) {
e.printStackTrace();
fail("Template parsing threw exception");
diff --git a/server/tests/src/com/vaadin/tests/design/ParseLayoutTest.java b/server/tests/src/com/vaadin/tests/design/ParseLayoutTest.java
index 07a0b218f7..c517b0e5e2 100644
--- a/server/tests/src/com/vaadin/tests/design/ParseLayoutTest.java
+++ b/server/tests/src/com/vaadin/tests/design/ParseLayoutTest.java
@@ -15,6 +15,8 @@
*/
package com.vaadin.tests.design;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -22,6 +24,7 @@ import java.io.InputStream;
import junit.framework.TestCase;
+import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
@@ -54,8 +57,10 @@ public class ParseLayoutTest extends TestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
- ctx = Design.parse(new FileInputStream(
- "server/tests/src/com/vaadin/tests/design/testFile.html"));
+ ctx = Design
+ .read(new FileInputStream(
+ "server/tests/src/com/vaadin/tests/design/testFile.html"),
+ null);
}
/*
@@ -78,15 +83,18 @@ public class ParseLayoutTest extends TestCase {
*/
@Test
public void testThatSerializationPreservesProperties() throws IOException {
- Document doc = Design.createHtml(ctx);
- DesignContext newContext = Design.parse(doc.toString());
- // Check that the elements can still be found by id and caption
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ Design.write(ctx, out);
+ ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+ DesignContext newContext = Design.read(in, null);
findElements(newContext);
checkHierarchy(newContext);
// Check the mapping from prefixes to package names using the html tree
String[] expectedPrefixes = { "my" };
String[] expectedPackageNames = { "com.addon.mypackage" };
int index = 0;
+
+ Document doc = Jsoup.parse(out.toString("UTF-8"));
Element head = doc.head();
for (Node child : head.childNodes()) {
if ("meta".equals(child.nodeName())) {
@@ -116,7 +124,7 @@ public class ParseLayoutTest extends TestCase {
LayoutTemplate template = new LayoutTemplate();
InputStream htmlFile = new FileInputStream(
"server/tests/src/com/vaadin/tests/design/testFile.html");
- Design.parse(htmlFile, template);
+ Design.read(htmlFile, template);
assertNotNull(template.getFirstButton());
assertNotNull(template.getSecondButton());
assertNotNull(template.getYetanotherbutton());
@@ -135,7 +143,7 @@ public class ParseLayoutTest extends TestCase {
InputStream htmlFile = new FileInputStream(
"server/tests/src/com/vaadin/tests/design/testFile.html");
try {
- Design.parse(htmlFile, template);
+ Design.read(htmlFile, template);
// we are expecting an exception
fail();
} catch (DesignException e) {
@@ -148,7 +156,7 @@ public class ParseLayoutTest extends TestCase {
* component hierarchy rooted at context.getComponentRoot().
*/
private void checkHierarchy(DesignContext context) {
- Component root = context.getComponentRoot();
+ Component root = context.getRootComponent();
VerticalLayout vlayout = (VerticalLayout) root;
int numComponents = vlayout.getComponentCount();
assertEquals("Wrong number of child components", 3, numComponents);
diff --git a/server/tests/src/com/vaadin/tests/design/TestLocale.java b/server/tests/src/com/vaadin/tests/design/TestLocale.java
index 8b46c9feff..b12e390aed 100644
--- a/server/tests/src/com/vaadin/tests/design/TestLocale.java
+++ b/server/tests/src/com/vaadin/tests/design/TestLocale.java
@@ -15,6 +15,7 @@
*/
package com.vaadin.tests.design;
+import java.io.ByteArrayInputStream;
import java.util.Locale;
import junit.framework.TestCase;
@@ -22,13 +23,15 @@ import junit.framework.TestCase;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.DocumentType;
import org.jsoup.nodes.Element;
+import org.jsoup.nodes.Node;
import com.vaadin.ui.Button;
+import com.vaadin.ui.Component;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;
-import com.vaadin.ui.declarative.DesignContext;
import com.vaadin.ui.declarative.Design;
+import com.vaadin.ui.declarative.DesignContext;
/**
* Tests the handling of the locale property in parsing and html generation.
@@ -71,9 +74,9 @@ public class TestLocale extends TestCase {
Label l2 = new Label();
l2.setLocale(Locale.CANADA);
hlayout2.addComponent(l2);
- ctx.setComponentRoot(vLayout);
+ ctx.setRootComponent(vLayout);
// create the html tree corresponding to the component hierarchy
- Document doc = Design.createHtml(ctx);
+ Document doc = componentToDoc(ctx);
// check the created html
Element body = doc.body();
Element evLayout = body.child(0);
@@ -101,6 +104,28 @@ public class TestLocale extends TestCase {
assertEquals("Wrong locale information.", "en_CA", el2.attr("locale"));
}
+ private Document componentToDoc(DesignContext dc) {
+ // Create the html tree skeleton.
+ Document doc = new Document("");
+ DocumentType docType = new DocumentType("html", "", "", "");
+ doc.appendChild(docType);
+ Element html = doc.createElement("html");
+ doc.appendChild(html);
+ html.appendChild(doc.createElement("head"));
+ Element body = doc.createElement("body");
+ html.appendChild(body);
+ dc.storePrefixes(doc);
+
+ // Append the design under <body> in the html tree. createNode
+ // creates the entire component hierarchy rooted at the
+ // given root node.
+ Component root = dc.getRootComponent();
+ Node rootNode = dc.createNode(root);
+ body.appendChild(rootNode);
+ return doc;
+
+ }
+
/*
* Checks that the locale of a component is set when the html element
* corresponding to the component specifies a locale.
@@ -131,8 +156,9 @@ public class TestLocale extends TestCase {
// parse the created document and check the constructed component
// hierarchy
- VerticalLayout vLayout = (VerticalLayout) Design.parse(
- doc.toString()).getComponentRoot();
+ String string = doc.html();
+ VerticalLayout vLayout = (VerticalLayout) Design
+ .read(new ByteArrayInputStream(string.getBytes()));
assertEquals("Wrong locale.", new Locale("en", "US"),
vLayout.getLocale());
HorizontalLayout hLayout = (HorizontalLayout) vLayout.getComponent(0);
diff --git a/server/tests/src/com/vaadin/tests/design/verticallayout-one-child.html b/server/tests/src/com/vaadin/tests/design/verticallayout-one-child.html
new file mode 100644
index 0000000000..4b0123b9ed
--- /dev/null
+++ b/server/tests/src/com/vaadin/tests/design/verticallayout-one-child.html
@@ -0,0 +1,3 @@
+<v-vertical-layout>
+ <v-button>OK</v-button>
+</v-vertical-layout>
diff --git a/server/tests/src/com/vaadin/tests/design/verticallayout-two-children.html b/server/tests/src/com/vaadin/tests/design/verticallayout-two-children.html
new file mode 100644
index 0000000000..87ae9eee8e
--- /dev/null
+++ b/server/tests/src/com/vaadin/tests/design/verticallayout-two-children.html
@@ -0,0 +1,4 @@
+<v-vertical-layout>
+ <v-text-field caption="Enter your name" />
+ <v-button>Say hello</v-button>
+</v-vertical-layout>