This patch also adds tests for PushState and URI Fragment navigation with CDI.tags/8.3.0.alpha1
@@ -19,7 +19,7 @@ | |||
<properties> | |||
<vaadin.spring.version>3.0.0.alpha1</vaadin.spring.version> | |||
<vaadin.testbench.version>5.1.1</vaadin.testbench.version> | |||
<vaadin.cdi.version>2.0.0</vaadin.cdi.version> | |||
<vaadin.cdi.version>3.0.0.alpha2</vaadin.cdi.version> | |||
<vaadin.context-menu.version>2.0.0</vaadin.context-menu.version> | |||
</properties> | |||
@@ -10,6 +10,9 @@ | |||
<artifactId>vaadin-test-cdi</artifactId> | |||
<packaging>war</packaging> | |||
<properties> | |||
<jetty.skip>true</jetty.skip> | |||
</properties> | |||
<dependencies> | |||
<dependency> | |||
@@ -32,26 +35,10 @@ | |||
<build> | |||
<finalName>ROOT</finalName> | |||
<plugins> | |||
<!-- Disable jetty-plugin --> | |||
<plugin> | |||
<groupId>org.eclipse.jetty</groupId> | |||
<artifactId>jetty-maven-plugin</artifactId> | |||
<version>${jetty.version}</version> | |||
<executions> | |||
<execution> | |||
<id>start-jetty</id> | |||
<phase /> | |||
</execution> | |||
<execution> | |||
<id>stop-jetty</id> | |||
<phase /> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.wildfly.plugins</groupId> | |||
<artifactId>wildfly-maven-plugin</artifactId> | |||
<version>1.0.2.Final</version> | |||
<version>1.2.0.Final</version> | |||
<executions> | |||
<execution> | |||
<id>start-wildfly</id> |
@@ -1,26 +0,0 @@ | |||
package com.vaadin.test.cdi; | |||
import javax.inject.Inject; | |||
import com.vaadin.annotations.Theme; | |||
import com.vaadin.cdi.CDIUI; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.ui.Button; | |||
import com.vaadin.ui.Notification; | |||
import com.vaadin.ui.UI; | |||
@Theme("valo") | |||
@CDIUI("") | |||
@SuppressWarnings("serial") | |||
public class MyVaadinUI extends UI { | |||
@Inject | |||
private ThankYouService service; | |||
@Override | |||
protected void init(VaadinRequest request) { | |||
setContent(new Button("Click Me", | |||
event -> Notification.show(service.getText()))); | |||
} | |||
} |
@@ -0,0 +1,76 @@ | |||
package com.vaadin.test.cdi.ui; | |||
import java.util.stream.Stream; | |||
import javax.enterprise.event.Observes; | |||
import javax.inject.Inject; | |||
import com.vaadin.cdi.AfterViewChange; | |||
import com.vaadin.cdi.CDINavigator; | |||
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; | |||
import com.vaadin.server.Page; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.ui.Button; | |||
import com.vaadin.ui.HorizontalLayout; | |||
import com.vaadin.ui.Notification; | |||
import com.vaadin.ui.Notification.Type; | |||
import com.vaadin.ui.Panel; | |||
import com.vaadin.ui.UI; | |||
import com.vaadin.ui.VerticalLayout; | |||
import com.vaadin.ui.themes.ValoTheme; | |||
public abstract class BaseUI extends UI { | |||
public static final String NAVIGATION_TEXT = "Navigated to: %s"; | |||
@Inject | |||
/* This should be automatic */ | |||
private CDINavigator navigator; | |||
@Override | |||
protected void init(VaadinRequest request) { | |||
Page.getCurrent().getStyles() | |||
.add(".vertical-wrap-flex { " | |||
+ "display: flex; flex-wrap: wrap; flex-direction: column;" | |||
+ "}"); | |||
VerticalLayout content = new VerticalLayout(); | |||
Panel container = new Panel(); | |||
HorizontalLayout naviBar = new HorizontalLayout(); | |||
content.addComponents(naviBar); | |||
content.addComponentsAndExpand(container); | |||
container.setSizeFull(); | |||
content.setSizeFull(); | |||
setContent(content); | |||
// Create navigation bar links | |||
Button firstLink = new Button("Default", | |||
e -> getNavigator().navigateTo("")); | |||
firstLink.addStyleName(ValoTheme.BUTTON_LINK); | |||
naviBar.addComponent(firstLink); | |||
Stream.of("param", "param/foo", "name/foo", "name/bar", "new", | |||
"persisting", "persisting/foo") | |||
.map(n -> new Button(n, e -> getNavigator().navigateTo(n))) | |||
.forEach(b -> { | |||
b.addStyleName(ValoTheme.BUTTON_LINK); | |||
naviBar.addComponent(b); | |||
}); | |||
// This should be automatic | |||
navigator.init(this, container); | |||
setNavigator(navigator); | |||
} | |||
protected void notifyNavigationToNonDefault( | |||
@Observes @AfterViewChange ViewChangeEvent event) { | |||
if (!event.getViewName().isEmpty()) { | |||
Notification.show( | |||
String.format(NAVIGATION_TEXT, event.getViewName()), | |||
Type.TRAY_NOTIFICATION); | |||
} | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
package com.vaadin.test.cdi.ui; | |||
import com.vaadin.annotations.PreserveOnRefresh; | |||
import com.vaadin.annotations.Theme; | |||
import com.vaadin.cdi.CDIUI; | |||
@Theme("valo") | |||
@CDIUI("") | |||
@SuppressWarnings("serial") | |||
@PreserveOnRefresh | |||
public class RootPathUI extends BaseUI { | |||
} |
@@ -0,0 +1,14 @@ | |||
package com.vaadin.test.cdi.ui; | |||
import com.vaadin.annotations.PreserveOnRefresh; | |||
import com.vaadin.annotations.Theme; | |||
import com.vaadin.cdi.CDIUI; | |||
import com.vaadin.navigator.PushStateNavigation; | |||
@Theme("valo") | |||
@SuppressWarnings("serial") | |||
@CDIUI("subpath") | |||
@PreserveOnRefresh | |||
@PushStateNavigation | |||
public class SubPathUI extends RootPathUI { | |||
} |
@@ -0,0 +1,8 @@ | |||
package com.vaadin.test.cdi.views; | |||
import com.vaadin.cdi.CDIView; | |||
import com.vaadin.cdi.ViewContextStrategy.Always; | |||
@CDIView(value = "new", contextStrategy = Always.class) | |||
public class AlwaysNewView extends GreetingView { | |||
} |
@@ -0,0 +1,24 @@ | |||
package com.vaadin.test.cdi.views; | |||
import javax.inject.Inject; | |||
import com.vaadin.cdi.CDIView; | |||
import com.vaadin.cdi.ViewContextStrategy.Always; | |||
import com.vaadin.navigator.View; | |||
import com.vaadin.test.cdi.ThankYouService; | |||
import com.vaadin.ui.Button; | |||
import com.vaadin.ui.Component; | |||
import com.vaadin.ui.Notification; | |||
@CDIView(value = "", contextStrategy = Always.class) | |||
public class DefaultView implements View { | |||
@Inject | |||
private ThankYouService service; | |||
@Override | |||
public Component getViewComponent() { | |||
return new Button("Click Me", | |||
e -> Notification.show(service.getText())); | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
package com.vaadin.test.cdi.views; | |||
import javax.enterprise.context.Dependent; | |||
@Dependent | |||
public class GreetingService { | |||
private int count; | |||
public String getGreeting(String name) { | |||
++count; | |||
if (name.isEmpty()) { | |||
return "Hello!"; | |||
} | |||
return "Hello, " + name + "!"; | |||
} | |||
public int getCallCount() { | |||
return count; | |||
} | |||
} |
@@ -0,0 +1,52 @@ | |||
package com.vaadin.test.cdi.views; | |||
import javax.inject.Inject; | |||
import com.vaadin.cdi.CDIView; | |||
import com.vaadin.navigator.View; | |||
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; | |||
import com.vaadin.ui.Alignment; | |||
import com.vaadin.ui.CssLayout; | |||
import com.vaadin.ui.Label; | |||
import com.vaadin.ui.VerticalLayout; | |||
public class GreetingView extends VerticalLayout implements View { | |||
public static final String CALL_COUNT_FORMAT = "Current call count: %d"; | |||
@Inject | |||
private GreetingService service; | |||
CssLayout greetingLog = new CssLayout(); | |||
Label callCount = new Label(""); | |||
public GreetingView() { | |||
addComponentsAndExpand(greetingLog); | |||
addComponent(callCount); | |||
setComponentAlignment(callCount, Alignment.BOTTOM_LEFT); | |||
greetingLog.setSizeFull(); | |||
greetingLog.addStyleName("vertical-wrap-flex"); | |||
greetingLog.setId("log"); | |||
callCount.setId("callCount"); | |||
setSizeFull(); | |||
setExpandRatio(greetingLog, 1.0f); | |||
setId(getClass().getAnnotation(CDIView.class).value()); | |||
} | |||
private void updateCallCount() { | |||
callCount.setValue( | |||
String.format(CALL_COUNT_FORMAT, service.getCallCount())); | |||
} | |||
private void greet(String name) { | |||
greetingLog.addComponent(new Label(service.getGreeting(name))); | |||
updateCallCount(); | |||
} | |||
@Override | |||
public void enter(ViewChangeEvent event) { | |||
greet(event.getParameters()); | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
package com.vaadin.test.cdi.views; | |||
import com.vaadin.cdi.CDIView; | |||
import com.vaadin.cdi.ViewContextStrategy.ViewName; | |||
@CDIView(value = "name", contextStrategy = ViewName.class) | |||
public class NameBasedView extends GreetingView { | |||
} |
@@ -0,0 +1,8 @@ | |||
package com.vaadin.test.cdi.views; | |||
import com.vaadin.cdi.CDIView; | |||
import com.vaadin.cdi.ViewContextStrategy.ViewNameAndParameters; | |||
@CDIView(value = "param", contextStrategy = ViewNameAndParameters.class) | |||
public class ParamBasedView extends GreetingView { | |||
} |
@@ -0,0 +1,10 @@ | |||
package com.vaadin.test.cdi.views; | |||
import com.vaadin.cdi.CDIView; | |||
import com.vaadin.cdi.UIScoped; | |||
import com.vaadin.cdi.ViewContextStrategy.Always; | |||
@UIScoped | |||
@CDIView(value = "persisting", contextStrategy = Always.class) | |||
public class UIScopedView extends GreetingView { | |||
} |
@@ -0,0 +1,88 @@ | |||
package com.vaadin.test.cdi; | |||
import java.util.List; | |||
import java.util.stream.Collectors; | |||
import org.junit.Assert; | |||
import org.junit.Test; | |||
import org.junit.runner.RunWith; | |||
import org.openqa.selenium.By; | |||
import com.vaadin.testbench.annotations.RunLocally; | |||
import com.vaadin.testbench.elements.ButtonElement; | |||
import com.vaadin.testbench.elements.CssLayoutElement; | |||
import com.vaadin.testbench.elements.LabelElement; | |||
import com.vaadin.testbench.parallel.Browser; | |||
import com.vaadin.testbench.parallel.ParallelRunner; | |||
import com.vaadin.testbench.parallel.ParallelTest; | |||
@RunWith(ParallelRunner.class) | |||
@RunLocally(Browser.PHANTOMJS) | |||
public class CDINavigationIT extends ParallelTest { | |||
protected static final String BASE_URL = "http://localhost:8080"; | |||
@Override | |||
public void setup() throws Exception { | |||
super.setup(); | |||
testBench().resizeViewPortTo(1024, 600); | |||
getDriver().get(getTestURL()); | |||
} | |||
protected String getUIPath() { | |||
return "/"; | |||
} | |||
protected String getViewSeparator() { | |||
return "#!"; | |||
} | |||
@Test | |||
public void testNavigation() { | |||
navigateTo("new"); | |||
navigateTo("persisting"); | |||
navigateTo("param/foo"); | |||
} | |||
@Test | |||
public void testReloadPage() { | |||
navigateTo("name/foo"); | |||
navigateTo("name/bar"); | |||
List<String> content = getLogContent(); | |||
getDriver().navigate().refresh(); | |||
Assert.assertTrue(isElementPresent(By.id("name"))); | |||
Assert.assertEquals("Content was lost when reloading", content, | |||
getLogContent()); | |||
} | |||
protected String getTestURL() { | |||
return BASE_URL + getUIPath(); | |||
} | |||
private List<String> getLogContent() { | |||
return $(CssLayoutElement.class).$$(LabelElement.class).all().stream() | |||
.map(LabelElement::getText).collect(Collectors.toList()); | |||
} | |||
protected void navigateTo(String state) { | |||
navigateTo(state, getUIPath(), getViewSeparator()); | |||
} | |||
protected void navigateTo(String state, String uiPath, | |||
String viewSeparator) { | |||
$(ButtonElement.class).caption(state).first().click(); | |||
String id = state; | |||
if (id.contains("/")) { | |||
id = id.substring(0, id.indexOf("/")); | |||
} | |||
Assert.assertTrue(isElementPresent(By.id(id))); | |||
Assert.assertEquals("Navigation to state '" + state + "' failed", | |||
BASE_URL + uiPath + viewSeparator + state, | |||
getDriver().getCurrentUrl()); | |||
} | |||
} |
@@ -0,0 +1,13 @@ | |||
package com.vaadin.test.cdi; | |||
public class CDIPushStateNavigationIT extends CDINavigationIT { | |||
protected String getUIPath() { | |||
return "/subpath"; | |||
} | |||
@Override | |||
protected String getViewSeparator() { | |||
return "/"; | |||
} | |||
} |
@@ -3,36 +3,197 @@ package com.vaadin.test.cdi; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertTrue; | |||
import org.junit.Before; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.stream.Collectors; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.openqa.selenium.phantomjs.PhantomJSDriver; | |||
import org.junit.runner.RunWith; | |||
import com.vaadin.test.cdi.ui.RootPathUI; | |||
import com.vaadin.test.cdi.views.GreetingService; | |||
import com.vaadin.test.cdi.views.GreetingView; | |||
import com.vaadin.testbench.ScreenshotOnFailureRule; | |||
import com.vaadin.testbench.TestBenchTestCase; | |||
import com.vaadin.testbench.annotations.RunLocally; | |||
import com.vaadin.testbench.elements.ButtonElement; | |||
import com.vaadin.testbench.elements.CssLayoutElement; | |||
import com.vaadin.testbench.elements.LabelElement; | |||
import com.vaadin.testbench.elements.NotificationElement; | |||
import com.vaadin.testbench.parallel.Browser; | |||
import com.vaadin.testbench.parallel.ParallelRunner; | |||
import com.vaadin.testbench.parallel.ParallelTest; | |||
@RunWith(ParallelRunner.class) | |||
@RunLocally(Browser.PHANTOMJS) | |||
public class VaadinCDISmokeIT extends ParallelTest { | |||
public class VaadinCDISmokeIT extends TestBenchTestCase { | |||
private static final String BASE_URL = "http://localhost:8080/"; | |||
@Rule | |||
public ScreenshotOnFailureRule rule = new ScreenshotOnFailureRule(this, | |||
true); | |||
@Before | |||
public void setup() { | |||
// Screenshot rule tears down the driver | |||
setDriver(new PhantomJSDriver()); | |||
@Override | |||
public void setup() throws Exception { | |||
super.setup(); | |||
testBench().resizeViewPortTo(1024, 600); | |||
} | |||
@Test | |||
public void testPageLoadsAndCanBeInterractedWith() { | |||
getDriver().navigate().to("http://localhost:8080/"); | |||
getDriver().navigate().to(BASE_URL); | |||
$(ButtonElement.class).first().click(); | |||
$(ButtonElement.class).caption("Click Me").first().click(); | |||
assertTrue($(NotificationElement.class).exists()); | |||
assertEquals(ThankYouServiceImpl.THANK_YOU_TEXT, | |||
$(NotificationElement.class).first().getText()); | |||
} | |||
@Test | |||
public void testAlwaysNewViewIsRecrated() { | |||
getDriver().navigate().to(BASE_URL); | |||
navigateAndGetLogContent("new"); | |||
// A new navigation event should happen when navigating to view again | |||
navigateAndGetLogContent("new"); | |||
assertEquals("GreetingService should've been only called once.", | |||
String.format(GreetingView.CALL_COUNT_FORMAT, 1), | |||
$(LabelElement.class).id("callCount").getText()); | |||
} | |||
@Test | |||
public void testParameterChangeRecreatedView() { | |||
GreetingService service = new GreetingService(); | |||
getDriver().navigate().to(BASE_URL); | |||
navigateAndGetLogContent("param"); | |||
assertEquals("Greeting service was not called with empty parameter.", | |||
service.getGreeting(""), $(CssLayoutElement.class).id("log") | |||
.$(LabelElement.class).first().getText()); | |||
// Navigation event is fired with same view and different parameters | |||
navigateAndGetLogContent("param", "foo"); | |||
assertEquals("Greeting service was not called with correct parameters.", | |||
service.getGreeting("foo"), $(CssLayoutElement.class).id("log") | |||
.$(LabelElement.class).first().getText()); | |||
assertEquals("GreetingService should've been only called once.", | |||
String.format(GreetingView.CALL_COUNT_FORMAT, 1), | |||
$(LabelElement.class).id("callCount").getText()); | |||
} | |||
@Test | |||
public void testParameterChangeUsesSameView() { | |||
GreetingService service = new GreetingService(); | |||
getDriver().navigate().to(BASE_URL); | |||
navigateAndGetLogContent("name", "foo"); | |||
assertEquals("Greeting service was not called with 'foo' parameter.", | |||
service.getGreeting("foo"), $(CssLayoutElement.class).id("log") | |||
.$(LabelElement.class).first().getText()); | |||
// Navigation event fired with same view and different parameters | |||
navigateAndGetLogContent("name", "bar"); | |||
assertEquals("GreetingService should've been only called twice.", | |||
String.format(GreetingView.CALL_COUNT_FORMAT, 2), | |||
$(LabelElement.class).id("callCount").getText()); | |||
assertEquals("Greeting service was not called with 'bar' parameter.", | |||
service.getGreeting("bar"), $(CssLayoutElement.class).id("log") | |||
.$(LabelElement.class).get(1).getText()); | |||
} | |||
@Test | |||
public void testUIScopedView() { | |||
GreetingService service = new GreetingService(); | |||
List<String> expectedLogContents = new ArrayList<>(); | |||
getDriver().navigate().to(BASE_URL); | |||
expectedLogContents | |||
.add(navigateAndGetLogContent("persisting", "foo", service)); | |||
// Navigation event should with same view and different parameters | |||
expectedLogContents | |||
.add(navigateAndGetLogContent("persisting", "", service)); | |||
// Navigating to another view should not lose the UI Scoped view. | |||
navigateAndGetLogContent("new"); | |||
expectedLogContents | |||
.add(navigateAndGetLogContent("persisting", "", service)); | |||
assertEquals("GreetingService unexpected call count", | |||
String.format(GreetingView.CALL_COUNT_FORMAT, | |||
service.getCallCount()), | |||
$(LabelElement.class).id("callCount").getText()); | |||
assertEquals("Unexpected contents in the log.", expectedLogContents, | |||
$(CssLayoutElement.class).id("log").$(LabelElement.class).all() | |||
.stream().map(LabelElement::getText) | |||
.collect(Collectors.toList())); | |||
} | |||
@Test | |||
public void testPreserveOnRefreshWithViewName() { | |||
GreetingService service = new GreetingService(); | |||
List<String> expectedLogContents = new ArrayList<>(); | |||
getDriver().navigate().to(BASE_URL); | |||
expectedLogContents | |||
.add(navigateAndGetLogContent("name", "foo", service)); | |||
// Navigation event should with same view and different parameters | |||
expectedLogContents | |||
.add(navigateAndGetLogContent("name", "bar", service)); | |||
// Reload the page. | |||
getDriver().navigate().refresh(); | |||
assertEquals("GreetingService should've been only called twice.", | |||
String.format(GreetingView.CALL_COUNT_FORMAT, | |||
service.getCallCount()), | |||
$(LabelElement.class).id("callCount").getText()); | |||
assertEquals("Unexpected contents in the log.", expectedLogContents, | |||
$(CssLayoutElement.class).id("log").$(LabelElement.class).all() | |||
.stream().map(LabelElement::getText) | |||
.collect(Collectors.toList())); | |||
} | |||
private void assertNavigationNotification(String string) { | |||
waitUntil(e -> isElementPresent(NotificationElement.class)); | |||
NotificationElement notification = $(NotificationElement.class).first(); | |||
assertEquals(String.format(RootPathUI.NAVIGATION_TEXT, string), | |||
notification.getText()); | |||
// Close all notifications. | |||
while (isElementPresent(NotificationElement.class)) { | |||
$(NotificationElement.class).first().close(); | |||
} | |||
} | |||
private String navigateAndGetLogContent(String viewName) { | |||
return navigateAndGetLogContent(viewName, "", new GreetingService()); | |||
} | |||
private String navigateAndGetLogContent(String viewName, String parameter) { | |||
return navigateAndGetLogContent(viewName, parameter, new GreetingService()); | |||
} | |||
private String navigateAndGetLogContent(String viewName, String parameter, | |||
GreetingService service) { | |||
String fullViewName = viewName | |||
+ (parameter.isEmpty() ? "" : "/" + parameter); | |||
$(ButtonElement.class).caption(fullViewName).first().click(); | |||
assertNavigationNotification(viewName); | |||
return service.getGreeting(parameter); | |||
} | |||
} |