/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wms.web.data;
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.*;
import java.io.ByteArrayInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.File;
import java.io.FileReader;
import java.io.Serializable;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.io.IOUtils;
import org.apache.wicket.Component;
import org.apache.wicket.Session;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.behavior.AbstractAjaxBehavior;
import org.apache.wicket.feedback.FeedbackMessage;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.util.tester.FormTester;
import org.apache.wicket.util.tester.WicketTesterHelper;
import org.geoserver.catalog.CatalogException;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.LegendInfo;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CatalogBuilder;
import org.geoserver.catalog.DataStoreInfo;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.ProjectionPolicy;
import org.geoserver.catalog.StyleInfo;
import org.geoserver.catalog.WMSLayerInfo;
import org.geoserver.catalog.WMSStoreInfo;
import org.geoserver.catalog.WorkspaceInfo;
import org.geoserver.catalog.event.CatalogAddEvent;
import org.geoserver.catalog.event.CatalogListener;
import org.geoserver.catalog.event.CatalogModifyEvent;
import org.geoserver.catalog.event.CatalogPostModifyEvent;
import org.geoserver.catalog.event.CatalogRemoveEvent;
import org.geoserver.data.test.MockData;
import org.geoserver.data.test.SystemTestData;
import org.geoserver.data.test.TestData;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.platform.GeoServerResourceLoader;
import org.geoserver.platform.resource.Paths;
import org.geoserver.platform.resource.Resource;
import org.geoserver.web.GeoServerWicketTestSupport;
import org.geoserver.web.wicket.GeoServerTablePanel;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.styling.Style;
import org.junit.Before;
import org.junit.Test;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.w3c.dom.Document;
public class StyleEditPageTest extends GeoServerWicketTestSupport {
StyleInfo buildingsStyle;
StyleEditPage edit;
private static final String STYLE_TO_MOVE_NAME = "testStyle";
private static final String STYLE_TO_MOVE_FILENAME = "testMoveStyle.sld";
private static final String STYLE_TO_MOVE_FILENAME_UPDATED = "testMoveStyleUpdated.sld";
StyleInfo styleInfoToMove;
@Before
public void setUp() throws Exception {
Catalog catalog = getCatalog();
login();
buildingsStyle = catalog.getStyleByName(MockData.BUILDINGS.getLocalPart());
if(buildingsStyle == null) {
// undo the rename performed in one of the test methods
StyleInfo si = catalog.getStyleByName("BuildingsNew");
if(si != null) {
si.setName(MockData.BUILDINGS.getLocalPart());
catalog.save(si);
}
buildingsStyle = catalog.getStyleByName(MockData.BUILDINGS.getLocalPart());
}
//Cleanup 'Deletes' layer
LayerInfo layer0 = catalog.getLayers().get(0);
StyleInfo defaultStyle = catalog.getStyleByName("Default");
layer0.setDefaultStyle(defaultStyle);
catalog.save(layer0);
//Create an inaccesible layer
DataStoreInfo ds = catalog.getStoreByName("sf", "unstore", DataStoreInfo.class);
if (ds == null) {
CatalogBuilder cb = new CatalogBuilder(catalog);
cb.setWorkspace(catalog.getWorkspaceByName("sf"));
ds = cb.buildDataStore("unstore");
catalog.add(ds);
FeatureTypeInfo ft = catalog.getFactory().createFeatureType();
ft.setName("unlayer");
ft.setStore(catalog.getStoreByName("unstore", DataStoreInfo.class));
ft.setCatalog(catalog);
ft.setNamespace(catalog.getNamespaceByPrefix("sf"));
ft.setSRS("EPSG:4326");
CoordinateReferenceSystem wgs84 = CRS.decode("EPSG:4326");
ft.setNativeCRS(wgs84);
ft.setLatLonBoundingBox(new ReferencedEnvelope(-110, 0, -60, 50, wgs84));
ft.setProjectionPolicy(ProjectionPolicy.FORCE_DECLARED);
catalog.add(ft);
LayerInfo ftl = catalog.getFactory().createLayer();
ftl.setResource(ft);
ftl.setDefaultStyle(getCatalog().getStyleByName("Default"));
catalog.add(ftl);
}
//Create a cascaded WMS Layer
WMSStoreInfo wms = catalog.getStoreByName("sf", "wmsstore", WMSStoreInfo.class);
if (wms == null) {
CatalogBuilder cb = new CatalogBuilder(catalog);
cb.setWorkspace(catalog.getWorkspaceByName("sf"));
wms = cb.buildWMSStore("wmsstore");
wms.setCapabilitiesURL("http://demo.opengeo.org/geoserver/wms?");
catalog.add(wms);
WMSLayerInfo wmr = catalog.getFactory().createWMSLayer();
wmr.setName("states");
wmr.setNativeName("topp:states");
wmr.setStore(catalog.getStoreByName("wmsstore", WMSStoreInfo.class));
wmr.setCatalog(catalog);
wmr.setNamespace(catalog.getNamespaceByPrefix("sf"));
wmr.setSRS("EPSG:4326");
CoordinateReferenceSystem wgs84 = CRS.decode("EPSG:4326");
wmr.setNativeCRS(wgs84);
wmr.setLatLonBoundingBox(new ReferencedEnvelope(-110, 0, -60, 50, wgs84));
wmr.setProjectionPolicy(ProjectionPolicy.FORCE_DECLARED);
catalog.add(wmr);
LayerInfo wml = catalog.getFactory().createLayer();
wml.setResource(wmr);
wml.setDefaultStyle(getCatalog().getStyleByName("Default"));
catalog.add(wml);
}
edit = new StyleEditPage(buildingsStyle);
tester.startPage(edit);
styleInfoToMove = catalog.getStyleByName("testStyle");
}
@Override
protected void onSetUp(SystemTestData testData) throws Exception {
super.onSetUp(testData);
testData.addStyle(STYLE_TO_MOVE_NAME, STYLE_TO_MOVE_FILENAME, this.getClass(), getCatalog());
}
@Test
public void testLoad() throws Exception {
tester.assertRenderedPage(StyleEditPage.class);
tester.assertNoErrorMessage();
tester.debugComponentTrees();
tester.assertComponent("styleForm:context:panel:name", TextField.class);
tester.assertComponent("styleForm:styleEditor:editorContainer:editorParent:editor", TextArea.class);
tester.assertVisible("styleForm:context:panel:upload");
//Load the legend
tester.executeAjaxEvent("styleForm:context:panel:legendPanel:externalGraphicContainer:showhide:show", "click");
tester.assertComponent("styleForm:context:panel:legendPanel", ExternalGraphicPanel.class);
tester.assertComponent("styleForm:context:panel:legendPanel:externalGraphicContainer:list:onlineResource", TextField.class);
tester.assertComponent("styleForm:context:panel:legendPanel:externalGraphicContainer:list:width", TextField.class);
tester.assertComponent("styleForm:context:panel:legendPanel:externalGraphicContainer:list:height", TextField.class);
tester.assertComponent("styleForm:context:panel:legendPanel:externalGraphicContainer:list:format", TextField.class);
tester.assertModelValue("styleForm:context:panel:name", "Buildings");
GeoServerResourceLoader loader = GeoServerExtensions.bean(GeoServerResourceLoader.class);
assertNotNull( loader );
String path = Paths.path("styles", Paths.convert(buildingsStyle.getFilename()));
Resource styleFile = loader.get(path);
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document d1 = db.parse( styleFile.in() );
//GEOS-3257, actually drag into xml and compare with xmlunit to avoid
// line ending problems
String xml = tester.getComponentFromLastRenderedPage("styleForm:styleEditor").getDefaultModelObjectAsString();
xml = xml.replaceAll("<","<").replaceAll(">",">").replaceAll(""", "\"");
Document d2 = db.parse( new ByteArrayInputStream(xml
.getBytes()));
assertXMLEqual(d1, d2);
}
@Test
public void testLoadLegend() {
}
@Test
public void testLayerAssociationsTab() {
LayerInfo l = getCatalog().getLayers().get(0);
assertFalse(l.getDefaultStyle() == buildingsStyle);
tester.executeAjaxEvent("styleForm:context:tabs-container:tabs:1:link", "click");
tester.assertComponent("styleForm:context:panel:layer.table", GeoServerTablePanel.class);
//Set the form value of the checkbox to true and force an ajax form update
FormTester form = tester.newFormTester("styleForm");
form.setValue("context:panel:layer.table:listContainer:items:1:itemProperties:2:component:default.selected", true);
AbstractAjaxBehavior behavior = (AbstractAjaxBehavior)WicketTesterHelper
.findBehavior(tester.getComponentFromLastRenderedPage(
"styleForm:context:panel:layer.table:listContainer:items:1:itemProperties:2:component:default.selected"),
AjaxFormComponentUpdatingBehavior.class);
tester.executeBehavior(behavior);
l = getCatalog().getLayers().get(0);
assertEquals(buildingsStyle, l.getDefaultStyle());
}
@Test
public void testLayerAssociationsMissingStyle() {
LayerInfo l = getCatalog().getLayers().get(0);
StyleInfo s = l.getDefaultStyle();
l.setDefaultStyle(null);
//Save against the facade to skip validation
getCatalog().getFacade().save(l);
try {
edit = new StyleEditPage(buildingsStyle);
tester.startPage(edit);
tester.executeAjaxEvent("styleForm:context:tabs-container:tabs:1:link", "click");
tester.assertComponent("styleForm:context:panel:layer.table", GeoServerTablePanel.class);
} finally {
l.setDefaultStyle(s);
getCatalog().save(l);
}
}
@Test
public void testLayerAttributesUnreachableLayer() throws Exception {
tester.executeAjaxEvent("styleForm:context:tabs-container:tabs:3:link", "click");
tester.executeAjaxEvent("styleForm:context:panel:changeLayer:link", "click");
tester.assertComponent("styleForm:popup:content:layer.table", GeoServerTablePanel.class);
tester.executeAjaxEvent("styleForm:popup:content:layer.table:navigatorBottom:navigator:last", "click");
tester.assertLabel("styleForm:popup:content:layer.table:listContainer:items:30:itemProperties:2:component:link:layer.name", "unlayer");
tester.executeAjaxEvent("styleForm:popup:content:layer.table:listContainer:items:30:itemProperties:2:component:link", "click");
tester.assertContains("Failed to load attribute list, internal error is:");
}
@Test
public void testLayerAttributesTabWMS() {
tester.executeAjaxEvent("styleForm:context:tabs-container:tabs:3:link", "click");
tester.executeAjaxEvent("styleForm:context:panel:changeLayer:link", "click");
tester.assertComponent("styleForm:popup:content:layer.table", GeoServerTablePanel.class);
//31 layers total, 25 layers per page; foo should not appear on page 1 or 2.
tester.assertContainsNot("wmsstore");
tester.executeAjaxEvent("styleForm:popup:content:layer.table:navigatorBottom:navigator:last", "click");
tester.assertContainsNot("wmsstore");
}
@Test
public void testMissingName() throws Exception {
FormTester form = tester.newFormTester("styleForm");
form.setValue("context:panel:name", "");
form.submit();
tester.assertRenderedPage(StyleEditPage.class);
tester.assertErrorMessages(new String[] {"Field 'Name' is required."});
}
@Test
public void testChangeName() throws Exception {
FormTester form = tester.newFormTester("styleForm");
form.setValue("context:panel:name", "BuildingsNew");
form.submit();
assertNull(getCatalog().getStyleByName("Buildings"));
assertNotNull(getCatalog().getStyleByName("BuildingsNew"));
}
@Test
public void testChangeNameAlreadyExists() throws Exception {
FormTester form = tester.newFormTester("styleForm");
form.setValue("context:panel:name", "Default");
tester.executeAjaxEvent("submit", "click");
tester.assertContains("java.lang.IllegalArgumentException: Style named 'Default' already exists");
tester.debugComponentTrees();
}
@Test
public void testValidate() throws Exception {
String xml =
"<StyledLayerDescriptor version='1.0.0' " +
" xsi:schemaLocation='http://www.opengis.net/sld StyledLayerDescriptor.xsd' " +
" xmlns='http://www.opengis.net/sld' " +
" xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>" +
" <NamedLayer>" +
" <Name>Style</Name>" +
" </NamedLayer>" +
"</StyledLayerDescriptor>";
// tester.debugComponentTrees();
tester.newFormTester("styleForm").setValue("styleEditor:editorContainer:editorParent:editor", xml);
tester.executeAjaxEvent("validate", "click");
tester.assertNoErrorMessage();
}
@Test
public void testValidateEntityExpansion() throws Exception {
String xml = IOUtils.toString(TestData.class.getResource("externalEntities.sld"), "UTF-8");
// tester.debugComponentTrees();
tester.newFormTester("styleForm").setValue("styleEditor:editorContainer:editorParent:editor", xml);
tester.executeAjaxEvent("validate", "click");
List<Serializable> messages = tester.getMessages(FeedbackMessage.ERROR);
assertEquals(1, messages.size());
String message = messages.get(0).toString();
assertThat(message, containsString("Entity resolution disallowed"));
assertThat(message, containsString("/this/file/does/not/exist"));
}
@Test
public void testGenerateTemplateFrenchLocale() throws Exception {
final Session session = tester.getSession();
try {
session.clear();
session.setLocale(Locale.FRENCH);
StyleEditPage edit = new StyleEditPage(buildingsStyle);
tester.startPage(edit);
// print(tester.getLastRenderedPage(), true, true);
// test the copy style link
tester.newFormTester("styleForm").select("context:panel:templates", 1);
tester.executeAjaxEvent("styleForm:context:panel:templates", "onchange");
Component generateLink = tester.getComponentFromLastRenderedPage("styleForm:context:panel:generate");
tester.executeAjaxEvent(generateLink, "onClick");
// check single quote in the message has been escaped
assertTrue(tester.getLastResponseAsString().contains("l\\'éditeur"));
} finally {
session.clear();
session.setLocale(Locale.getDefault());
}
}
@Test
public void testCopyStyleFrenchLocale() throws Exception {
final Session session = tester.getSession();
try {
session.clear();
session.setLocale(Locale.FRENCH);
StyleEditPage edit = new StyleEditPage(buildingsStyle);
tester.startPage(edit);
// print(tester.getLastRenderedPage(), true, true);
// test the copy style link
tester.newFormTester("styleForm").select("context:panel:existingStyles", 1);
tester.executeAjaxEvent("styleForm:context:panel:existingStyles", "onchange");
Component copyLink = tester.getComponentFromLastRenderedPage("styleForm:context:panel:copy");
tester.executeAjaxEvent(copyLink, "onClick");
// check single quote in the message has been escaped
assertTrue(tester.getLastResponseAsString().contains("l\\'éditeur"));
} finally {
session.clear();
session.setLocale(Locale.getDefault());
}
}
/**
* Test that while editing a style, the user can create and then discard a legend without ever saving it.
*/
@Test
public void testDiscardNewLegendInfo() {
tester.assertRenderedPage(StyleEditPage.class);
tester.assertNoErrorMessage();
// Show the legend panel (The test style does not initially have a legend)
tester.executeAjaxEvent(
"styleForm:context:panel:legendPanel:externalGraphicContainer:showhide:show",
"click");
// Assert that the legend panel components exist
tester.assertComponent("styleForm:context:panel:legendPanel", ExternalGraphicPanel.class);
tester.assertComponent(
"styleForm:context:panel:legendPanel:externalGraphicContainer:list:onlineResource",
TextField.class);
tester.assertComponent(
"styleForm:context:panel:legendPanel:externalGraphicContainer:list:width",
TextField.class);
tester.assertComponent(
"styleForm:context:panel:legendPanel:externalGraphicContainer:list:height",
TextField.class);
tester.assertComponent(
"styleForm:context:panel:legendPanel:externalGraphicContainer:list:format",
TextField.class);
// Hide the legend panel (= "Discard Legend")
tester.executeAjaxEvent(
"styleForm:context:panel:legendPanel:externalGraphicContainer:showhide:hide",
"click");
tester.assertNoErrorMessage();
// Submit the style (no legend should be saved)
tester.executeAjaxEvent("submit", "click");
StyleInfo style = getCatalog().getStyleByName(MockData.BUILDINGS.getLocalPart());
assertNotNull(style);
assertNull(style.getLegend());
}
/**
* Test that while editing a style, the user can discard a previously saved legend.
*/
@Test
public void testDiscardExistingLegend() throws IOException, URISyntaxException {
// Create a legend for the style
StyleInfo style = getCatalog().getStyleByName(MockData.BUILDINGS.getLocalPart());
LegendInfo legendInfo = getCatalog().getFactory().createLegend();
legendInfo.setFormat("jpg");
legendInfo.setOnlineResource("test.jpg");
legendInfo.setHeight(100);
legendInfo.setWidth(100);
style.setLegend(legendInfo);
getCatalog().save(style);
// Reload the page
tester.startPage(
new StyleEditPage(getCatalog().getStyleByName(MockData.BUILDINGS.getLocalPart())));
// Make sure the legend fields exist and are populated as expected
tester.assertComponent(
"styleForm:context:panel:legendPanel:externalGraphicContainer:list:onlineResource",
TextField.class);
tester.assertModelValue(
"styleForm:context:panel:legendPanel:externalGraphicContainer:list:onlineResource",
"test.jpg");
tester.assertComponent(
"styleForm:context:panel:legendPanel:externalGraphicContainer:list:width",
TextField.class);
tester.assertModelValue(
"styleForm:context:panel:legendPanel:externalGraphicContainer:list:width", 100);
tester.assertComponent(
"styleForm:context:panel:legendPanel:externalGraphicContainer:list:height",
TextField.class);
tester.assertModelValue(
"styleForm:context:panel:legendPanel:externalGraphicContainer:list:height", 100);
tester.assertComponent(
"styleForm:context:panel:legendPanel:externalGraphicContainer:list:format",
TextField.class);
tester.assertModelValue(
"styleForm:context:panel:legendPanel:externalGraphicContainer:list:format", "jpg");
// Hide the legend panel (= "Discard Legend")
tester.executeAjaxEvent(
"styleForm:context:panel:legendPanel:externalGraphicContainer:showhide:hide",
"click");
// Submit the form. (The legend should be discarded)
FormTester form = tester.newFormTester("styleForm", false);
form.submit();
tester.assertNoErrorMessage();
style = getCatalog().getStyleByName(MockData.BUILDINGS.getLocalPart());
assertNotNull(style);
assertNull(style.getLegend());
}
/**
* Test that while editing a style, the user can discard a legend, even if the legend has invalid values at the time, and then continue to save
* the style.
*/
@Test
public void testDiscardLegendWithBadValues() throws IOException, URISyntaxException {
tester.executeAjaxEvent(
"styleForm:context:panel:legendPanel:externalGraphicContainer:showhide:show",
"click");
// Make sure the fields we are editing actually exist
tester.assertComponent(
"styleForm:context:panel:legendPanel:externalGraphicContainer:list:onlineResource",
TextField.class);
tester.assertComponent(
"styleForm:context:panel:legendPanel:externalGraphicContainer:list:width",
TextField.class);
tester.assertComponent(
"styleForm:context:panel:legendPanel:externalGraphicContainer:list:height",
TextField.class);
tester.assertComponent(
"styleForm:context:panel:legendPanel:externalGraphicContainer:list:format",
TextField.class);
// Set some bad values for the legend
FormTester form = tester.newFormTester("styleForm", false);
form.setValue("context:panel:legendPanel:externalGraphicContainer:list:onlineResource",
"missing.ong");
form.setValue("context:panel:legendPanel:externalGraphicContainer:list:width", "-100");
form.setValue("context:panel:legendPanel:externalGraphicContainer:list:height", "");
form.setValue("context:panel:legendPanel:externalGraphicContainer:list:format",
"bad/value");
// Hide the legend panel (= "Discard Legend")
tester.executeAjaxEvent(
"styleForm:context:panel:legendPanel:externalGraphicContainer:showhide:hide",
"click");
// Refresh the state of the FormTester after the executeAjaxEvent
form = tester.newFormTester("styleForm", false);
// Submit the form. (The bad legend values should no longer be set).
form.submit();
tester.assertNoErrorMessage();
StyleInfo style = getCatalog().getStyleByName(MockData.BUILDINGS.getLocalPart());
assertNotNull(style);
assertNull(style.getLegend());
}
/*
* Test that a user can update the .sld file contents and move the style into a workspace in a single edit.
*/
@Test
public void testMoveWorkspaceAndEdit() throws Exception {
// add catalog listener so we can validate the style modified event
final boolean[] gotValidEvent = {false};
getCatalog().addListener(new CatalogListener() {
@Override
public void handleAddEvent(CatalogAddEvent event) throws CatalogException {
// not interest, ignore this events
}
@Override
public void handleRemoveEvent(CatalogRemoveEvent event) throws CatalogException {
// not interest, ignore this events
}
@Override
public void handleModifyEvent(CatalogModifyEvent event) throws CatalogException {
// not interest, ignore this events
}
@Override
public void handlePostModifyEvent(CatalogPostModifyEvent event) throws CatalogException {
assertThat(event, notNullValue());
assertThat(event.getSource(), notNullValue());
if (!(event.getSource() instanceof StyleInfo)) {
// only interested in style info events
return;
}
try {
// get the associated style and check that you got the correct content
StyleInfo styleInfo = (StyleInfo) event.getSource();
assertThat(styleInfo, notNullValue());
Style style = getCatalog().getResourcePool().getStyle(styleInfo);
assertThat(style, notNullValue());
assertThat(style.featureTypeStyles().size(), is(2));
// ok everything looks good
gotValidEvent[0] = true;
} catch (Exception exception) {
LOGGER.log(Level.SEVERE, "Error handling catalog modified style event.", exception);
}
}
@Override
public void reloaded() {
// not interest, ignore this events
}
});
edit = new StyleEditPage(styleInfoToMove);
tester.startPage(edit);
// Before the edit, the style should have one <FeatureTypeStyle>
assertEquals(1, styleInfoToMove.getStyle().featureTypeStyles().size());
FormTester form = tester.newFormTester("styleForm", false);
// Update the workspace (select "sf" from the dropdown)
DropDownChoice<WorkspaceInfo> typeDropDown = (DropDownChoice<WorkspaceInfo>) tester
.getComponentFromLastRenderedPage("styleForm:context:panel:workspace");
for (int wsIdx = 0; wsIdx < typeDropDown.getChoices().size(); wsIdx++) {
WorkspaceInfo ws = typeDropDown.getChoices().get(wsIdx);
if ("sf".equalsIgnoreCase(ws.getName())) {
form.select("context:panel:workspace", wsIdx);
break;
}
}
// Update the raw style contents (the new style has TWO <FeatureTypeStyle> entries).
File styleFile = new File(getClass().getResource(STYLE_TO_MOVE_FILENAME_UPDATED).toURI());
String updatedSld = IOUtils.toString(new FileReader(styleFile)).replaceAll("\r\n", "\n")
.replaceAll("\r", "\n");
form.setValue("styleEditor:editorContainer:editorParent:editor", updatedSld);
// Submit the form and verify that both the new workspace and new rawStyle saved.
form.submit();
StyleInfo si = getCatalog().getStyleByName(getCatalog().getWorkspaceByName("sf"),
STYLE_TO_MOVE_NAME);
assertNotNull(si);
assertNotNull(si.getWorkspace());
assertEquals("sf", si.getWorkspace().getName());
assertEquals(2, si.getStyle().featureTypeStyles().size());
// check the correct style modified event was published
assertThat(gotValidEvent[0], is(true));
}
@Test
public void applyThenSubmit() throws Exception {
tester.executeAjaxEvent("apply", "click");
tester.executeAjaxEvent("submit", "click");
tester.assertNoErrorMessage();
}
}