소스 검색

Support for HTML5 push/replaceState for proper deep linking features (#8116)

* Added support for HTML5 push/replaceState for proper deep linkin features

* Automated test script now works at least on chrome

* Uses html5 push/popstate to implement uri fragment feature

* fire legacy fragment change events also via popstate events rpc calls
* send new fragments via pushstate mechanism

* formatting

* Formatting and adding test and workaround for IE bug

* Formatting and depracated UriFragmentListener

* Aligned naming in the new API

* Ignored IE due to web driver bug

Tested a workaround with javascript based window.location.href fetch,
but that don’t seem to work stable enough.
tags/8.0.0.beta2
Matti Tahvonen 7 년 전
부모
커밋
62c44dd77e

+ 0
- 63
client/src/main/java/com/vaadin/client/ui/VUI.java 파일 보기

@@ -26,11 +26,7 @@ import com.google.gwt.event.dom.client.ScrollHandler;
import com.google.gwt.event.logical.shared.HasResizeHandlers;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.SimplePanel;
@@ -46,7 +42,6 @@ import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
import com.vaadin.client.ui.TouchScrollDelegate.TouchScrollHandler;
import com.vaadin.client.ui.ui.UIConnector;
import com.vaadin.shared.ApplicationConstants;
import com.vaadin.shared.ui.ui.UIConstants;

/**
*
@@ -99,51 +94,8 @@ public class VUI extends SimplePanel implements ResizeHandler,
/** For internal use only. May be removed or replaced in the future. */
public boolean resizeLazy = false;

private HandlerRegistration historyHandlerRegistration;

private TouchScrollHandler touchScrollHandler;

/**
* The current URI fragment, used to avoid sending updates if nothing has
* changed.
* <p>
* For internal use only. May be removed or replaced in the future.
*/
public String currentFragment;

/**
* Listener for URI fragment changes. Notifies the server of the new value
* whenever the value changes.
*/
private final ValueChangeHandler<String> historyChangeHandler = new ValueChangeHandler<String>() {

@Override
public void onValueChange(ValueChangeEvent<String> event) {
String newFragment = event.getValue();

// Send the location to the server if the fragment has changed
// and flush active connectors in UI.
if (!newFragment.equals(currentFragment) && connection != null) {
/*
* Ensure the fragment is properly encoded in all browsers
* (#10769)
*
* createUrlBuilder does not properly pass an empty fragment to
* UrlBuilder on Webkit browsers so do it manually (#11686)
*/
String location = Window.Location.createUrlBuilder()
.setHash(URL
.decodeQueryString(Window.Location.getHash()))
.buildString();

currentFragment = newFragment;
connection.flushActiveConnector();
connection.updateVariable(id, UIConstants.LOCATION_VARIABLE,
location, true);
}
}
};

private VLazyExecutor delayedResizeExecutor = new VLazyExecutor(200,
new ScheduledCommand() {

@@ -186,21 +138,6 @@ public class VUI extends SimplePanel implements ResizeHandler,
}
}

@Override
protected void onAttach() {
super.onAttach();
historyHandlerRegistration = History
.addValueChangeHandler(historyChangeHandler);
currentFragment = History.getToken();
}

@Override
protected void onDetach() {
super.onDetach();
historyHandlerRegistration.removeHandler();
historyHandlerRegistration = null;
}

/**
* Stop monitoring for parent element resizes.
*/

+ 38
- 30
client/src/main/java/com/vaadin/client/ui/ui/UIConnector.java 파일 보기

@@ -43,18 +43,17 @@ import com.google.gwt.event.dom.client.ScrollHandler;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.Window.Location;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ConnectorHierarchyChangeEvent;
import com.vaadin.client.Focusable;
@@ -105,6 +104,8 @@ import com.vaadin.shared.ui.ui.UIState;
import com.vaadin.shared.util.SharedUtil;
import com.vaadin.ui.UI;

import elemental.client.Browser;

@Connect(value = UI.class, loadStyle = LoadStyle.EAGER)
public class UIConnector extends AbstractSingleComponentContainerConnector
implements Paintable, MayScrollChildren {
@@ -115,6 +116,12 @@ public class UIConnector extends AbstractSingleComponentContainerConnector

private HandlerRegistration windowOrderRegistration;

/*
* Used to workaround IE bug related to popstate events and certain fragment
* only changes
*/
private String currentLocation;

private final StateChangeHandler childStateChangeHandler = new StateChangeHandler() {
@Override
public void onStateChanged(StateChangeEvent stateChangeEvent) {
@@ -232,6 +239,28 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
}
}
});

Browser.getWindow().setOnpopstate(evt -> {
final String newLocation = Browser.getWindow().getLocation()
.toString();
getRpcProxy(UIServerRpc.class).popstate(newLocation);
currentLocation = newLocation;
});
// IE doesn't fire popstate correctly with certain hash changes.
// Simulate the missing event with History handler.
if (BrowserInfo.get().isIE()) {
History.addValueChangeHandler(evt -> {
final String newLocation = Browser.getWindow().getLocation()
.toString();
if (!newLocation.equals(currentLocation)) {
currentLocation = newLocation;
getRpcProxy(UIServerRpc.class).popstate(
Browser.getWindow().getLocation().toString());
}
});
currentLocation = Browser.getWindow().getLocation().toString();
}

}

private native void open(String url, String name)
@@ -402,34 +431,13 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
scrollIntoView(connector);
}

if (uidl.hasAttribute(UIConstants.LOCATION_VARIABLE)) {
String location = uidl
.getStringAttribute(UIConstants.LOCATION_VARIABLE);
String newFragment;

int fragmentIndex = location.indexOf('#');
if (fragmentIndex >= 0) {
// Decode fragment to avoid double encoding (#10769)
newFragment = URL.decodePathSegment(
location.substring(fragmentIndex + 1));

if (newFragment.isEmpty()
&& Location.getHref().indexOf('#') == -1) {
// Ensure there is a trailing # even though History and
// Location.getHash() treat null and "" the same way.
Location.assign(Location.getHref() + "#");
}
} else {
// No fragment in server-side location, but can't completely
// remove the browser fragment since that would reload the page
newFragment = "";
}

getWidget().currentFragment = newFragment;

if (!newFragment.equals(History.getToken())) {
History.newItem(newFragment, true);
}
if (uidl.hasAttribute(UIConstants.ATTRIBUTE_PUSH_STATE)) {
Browser.getWindow().getHistory().pushState(null, "",
uidl.getStringAttribute(UIConstants.ATTRIBUTE_PUSH_STATE));
}
if (uidl.hasAttribute(UIConstants.ATTRIBUTE_REPLACE_STATE)) {
Browser.getWindow().getHistory().replaceState(null, "", uidl
.getStringAttribute(UIConstants.ATTRIBUTE_REPLACE_STATE));
}

if (firstPaint) {

+ 1
- 2
client/src/main/resources/com/vaadin/DefaultWidgetSet.gwt.xml 파일 보기

@@ -8,8 +8,7 @@

<inherits name="com.vaadin.Vaadin" />

<!-- Elemental is used for handling Json only -->
<inherits name="elemental.Json" />
<inherits name="elemental.Elemental" />

<inherits name="com.google.gwt.precompress.Precompress" />


+ 197
- 6
server/src/main/java/com/vaadin/server/Page.java 파일 보기

@@ -251,7 +251,9 @@ public class Page implements Serializable {
* changes.
*
* @see Page#addUriFragmentChangedListener(UriFragmentChangedListener)
* @deprecated Use {@link PopStateListener} instead
*/
@Deprecated
@FunctionalInterface
public interface UriFragmentChangedListener extends Serializable {
/**
@@ -272,6 +274,32 @@ public class Page implements Serializable {
.findMethod(Page.UriFragmentChangedListener.class,
"uriFragmentChanged", UriFragmentChangedEvent.class);

/**
* Listener that that gets notified when the URI of the page changes due to
* back/forward functionality of the browser.
*
* @see Page#addPopStateListener(PopStateListener)
* @since 8.0
*/
@FunctionalInterface
public interface PopStateListener extends Serializable {
/**
* Event handler method invoked when the URI fragment of the page
* changes. Please note that the initial URI fragment has already been
* set when a new UI is initialized, so there will not be any initial
* event for listeners added during {@link UI#init(VaadinRequest)}.
*
* @see Page#addUriFragmentChangedListener(UriFragmentChangedListener)
*
* @param event
* the URI fragment changed event
*/
public void uriChanged(PopStateEvent event);
}

private static final Method URI_CHANGED_METHOD = ReflectTools.findMethod(
Page.PopStateListener.class, "uriChanged", PopStateEvent.class);

/**
* Resources to be opened automatically on next repaint. The list is
* automatically cleared when it has been sent to the client.
@@ -328,6 +356,53 @@ public class Page implements Serializable {
}
}

/**
* Event fired when the URI of a <code>Page</code> changes (aka HTML 5
* popstate event) on the client side due to browsers back/forward
* functionality.
*
* @see Page#addPopStateListener(PopStateListener)
* @since 8.0
*/
public static class PopStateEvent extends EventObject {

/**
* The new URI as String
*/
private final String uri;

/**
* Creates a new instance of PopstateEvent.
*
* @param source
* the Source of the event.
* @param uri
* the new uri
*/
public PopStateEvent(Page source, String uri) {
super(source);
this.uri = uri;
}

/**
* Gets the page in which the uri has changed.
*
* @return the page in which the uri has changed
*/
public Page getPage() {
return (Page) getSource();
}

/**
* Get the new URI
*
* @return the new uri
*/
public String getUri() {
return uri;
}
}

@FunctionalInterface
private static interface InjectedStyle extends Serializable {
public void paint(int id, PaintTarget target) throws PaintException;
@@ -483,6 +558,9 @@ public class Page implements Serializable {

private String windowName;

private String newPushState;
private String newReplaceState;

public Page(UI uI, PageState state) {
this.uI = uI;
this.state = state;
@@ -516,13 +594,37 @@ public class Page implements Serializable {
* @param listener
* the URI fragment listener to add
* @return a registration object for removing the listener
* @deprecated Use {@link Page#addPopStateListener(PopStateListener)}
* instead
*/
@Deprecated
public Registration addUriFragmentChangedListener(
Page.UriFragmentChangedListener listener) {
return addListener(UriFragmentChangedEvent.class, listener,
URI_FRAGMENT_CHANGED_METHOD);
}

/**
* Adds a listener that gets notified every time the URI of this page is
* changed due to back/forward functionality of the browser.
* <p>
* Note that one only gets notified when the back/forward button affects
* history changes with-in same UI, created by
* {@link Page#pushState(String)} or {@link Page#replaceState(String)}
* functions.
*
* @see #getLocation()
* @see Registration
*
* @param listener
* the Popstate listener to add
* @return a registration object for removing the listener
* @since 8.0
*/
public Registration addPopStateListener(Page.PopStateListener listener) {
return addListener(PopStateEvent.class, listener, URI_CHANGED_METHOD);
}

/**
* Removes a URI fragment listener that was previously added to this page.
*
@@ -580,6 +682,7 @@ public class Page implements Serializable {
try {
location = new URI(location.getScheme(),
location.getSchemeSpecificPart(), newUriFragment);
pushState(location);
} catch (URISyntaxException e) {
// This should not actually happen as the fragment syntax is not
// constrained
@@ -588,7 +691,6 @@ public class Page implements Serializable {
if (fireEvents) {
fireEvent(new UriFragmentChangedEvent(this, newUriFragment));
}
uI.markAsDirty();
}

private void fireEvent(EventObject event) {
@@ -866,9 +968,14 @@ public class Page implements Serializable {
notifications = null;
}

if (location != null) {
target.addAttribute(UIConstants.LOCATION_VARIABLE,
location.toString());
if (newPushState != null) {
target.addAttribute(UIConstants.ATTRIBUTE_PUSH_STATE, newPushState);
newPushState = null;
}
if (newReplaceState != null) {
target.addAttribute(UIConstants.ATTRIBUTE_REPLACE_STATE,
newReplaceState);
newReplaceState = null;
}

if (styles != null) {
@@ -931,6 +1038,84 @@ public class Page implements Serializable {
return location;
}

/**
* Updates the browsers URI without causing actual page change. This method
* is useful if you wish implement "deep linking" to your application.
* Calling the method also adds a new entry to clients browser history and
* you can further use {@link PopStateListener} to track the usage of
* back/forward feature in browser.
* <p>
* Note, the current implementation supports setting only one new uri in one
* user interaction.
*
* @param uri
* to be used for pushState operation. The URI is resolved over
* the current location. If the given URI is absolute, it must be
* of same origin as the current URI or the browser will not
* accept the new value.
* @since 8.0
*/
public void pushState(String uri) {
newPushState = uri;
uI.markAsDirty();
location = location.resolve(uri);
}

/**
* Updates the browsers URI without causing actual page change. This method
* is useful if you wish implement "deep linking" to your application.
* Calling the method also adds a new entry to clients browser history and
* you can further use {@link PopStateListener} to track the usage of
* back/forward feature in browser.
* <p>
* Note, the current implementation supports setting only one new uri in one
* user interaction.
*
* @param uri
* the URI to be used for pushState operation. The URI is
* resolved over the current location. If the given URI is
* absolute, it must be of same origin as the current URI or the
* browser will not accept the new value.
* @since 8.0
*/
public void pushState(URI uri) {
pushState(uri.toString());
}

/**
* Updates the browsers URI without causing actual page change in the same
* way as {@link #pushState(String)}, but does not add new entry to browsers
* history.
*
* @param uri
* the URI to be used for replaceState operation. The URI is
* resolved over the current location. If the given URI is
* absolute, it must be of same origin as the current URI or the
* browser will not accept the new value.
* @since 8.0
*/
public void replaceState(String uri) {
newReplaceState = uri;
uI.markAsDirty();
location = location.resolve(uri);
}

/**
* Updates the browsers URI without causing actual page change in the same
* way as {@link #pushState(URI)}, but does not add new entry to browsers
* history.
*
* @param uri
* the URI to be used for replaceState operation. The URI is
* resolved over the current location. If the given URI is
* absolute, it must be of same origin as the current URI or the
* browser will not accept the new value.
* @since 8.0
*/
public void replaceState(URI uri) {
replaceState(uri.toString());
}

/**
* For internal use only. Used to update the server-side location when the
* client-side location changes.
@@ -943,7 +1128,7 @@ public class Page implements Serializable {
*/
@Deprecated
public void updateLocation(String location) {
updateLocation(location, true);
updateLocation(location, true, false);
}

/**
@@ -957,8 +1142,11 @@ public class Page implements Serializable {
* @param fireEvents
* whether to fire {@link UriFragmentChangedEvent} if the URI
* fragment changes
* @param firePopstate
* whether to fire {@link PopStateEvent}
*/
public void updateLocation(String location, boolean fireEvents) {
public void updateLocation(String location, boolean fireEvents,
boolean firePopstate) {
try {
String oldUriFragment = this.location.getFragment();
this.location = new URI(location);
@@ -967,6 +1155,9 @@ public class Page implements Serializable {
&& !SharedUtil.equals(oldUriFragment, newUriFragment)) {
fireEvent(new UriFragmentChangedEvent(this, newUriFragment));
}
if (firePopstate) {
fireEvent(new PopStateEvent(this, location));
}
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}

+ 8
- 7
server/src/main/java/com/vaadin/ui/UI.java 파일 보기

@@ -193,6 +193,12 @@ public abstract class UI extends AbstractSingleComponentContainer
public void acknowledge() {
// Nothing to do, just need the message to be sent and processed
}

@Override
public void popstate(String uri) {
getPage().updateLocation(uri, true, true);
}
};
private DebugWindowServerRpc debugRpc = new DebugWindowServerRpc() {
@Override
@@ -437,11 +443,6 @@ public abstract class UI extends AbstractSingleComponentContainer
actionManager.handleActions(variables, this);
}

if (variables.containsKey(UIConstants.LOCATION_VARIABLE)) {
String location = (String) variables
.get(UIConstants.LOCATION_VARIABLE);
getPage().updateLocation(location, true);
}
}

/*
@@ -789,10 +790,10 @@ public abstract class UI extends AbstractSingleComponentContainer
int newWidth = page.getBrowserWindowWidth();
int newHeight = page.getBrowserWindowHeight();

page.updateLocation(oldLocation.toString(), false);
page.updateLocation(oldLocation.toString(), false, false);
page.updateBrowserWindowSize(oldWidth, oldHeight, false);

page.updateLocation(newLocation.toString(), true);
page.updateLocation(newLocation.toString(), true, false);
page.updateBrowserWindowSize(newWidth, newHeight, true);
}


+ 4
- 1
shared/src/main/java/com/vaadin/shared/ui/ui/UIConstants.java 파일 보기

@@ -28,7 +28,10 @@ public class UIConstants implements Serializable {
public static final String NOTIFICATION_HTML_CONTENT_NOT_ALLOWED = "useplain";

@Deprecated
public static final String LOCATION_VARIABLE = "location";
public static final String ATTRIBUTE_PUSH_STATE = "ps";

@Deprecated
public static final String ATTRIBUTE_REPLACE_STATE = "rs";

@Deprecated
public static final String ATTRIBUTE_NOTIFICATION_STYLE = "style";

+ 2
- 0
shared/src/main/java/com/vaadin/shared/ui/ui/UIServerRpc.java 파일 보기

@@ -35,6 +35,8 @@ public interface UIServerRpc extends ClickRpc, ServerRpc {
* should always be called to ensure the message is flushed right away.
*/
public void poll();
public void popstate(String uri);

@NoLoadingIndicator
public void acknowledge();

+ 80
- 0
uitest/src/main/java/com/vaadin/tests/components/ui/PushStateAndReplaceState.java 파일 보기

@@ -0,0 +1,80 @@
package com.vaadin.tests.components.ui;

import java.net.URI;

import com.vaadin.server.Page;
import com.vaadin.server.Page.PopStateEvent;
import com.vaadin.server.Page.PopStateListener;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.Label;
import com.vaadin.ui.Notification;

public class PushStateAndReplaceState extends AbstractReindeerTestUI {

private final Label locationLabel = new Label();
private CheckBox replace;

@Override
protected void setup(VaadinRequest request) {
locationLabel.setId("locationLabel");
addComponent(locationLabel);
updateLabel();

getPage().addPopStateListener(new PopStateListener() {

@Override
public void uriChanged(PopStateEvent event) {
Notification.show("Popstate event");
updateLabel();
}
});

replace = new CheckBox("replace");
replace.setId("replace");
addComponent(replace);

addComponent(createButton("test", "Move to ./test",
Page.getCurrent().getLocation().toString() + "/test"));
addComponent(createButton("X", "Move to X", "X"));
addComponent(createButton("root_X", "Move to /X", "/X"));
}

private Button createButton(String id, String caption,
final String newUri) {
Button button = new Button(caption, new Button.ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
if (replace.getValue()) {
getPage().replaceState(newUri);
} else {
getPage().pushState(newUri);
}
updateLabel();
}
});

button.setId(id);

return button;
}

private void updateLabel() {
URI location = getPage().getLocation();
locationLabel.setValue("Current Location: " + location.toString());
}

@Override
public String getTestDescription() {
return "Modern web framework shouldn't force you to use hashbang style urls for deep linking";
}

@Override
protected Integer getTicketNumber() {
return null;
}

}

+ 8
- 0
uitest/src/main/java/com/vaadin/tests/components/ui/UriFragment.java 파일 보기

@@ -1,5 +1,6 @@
package com.vaadin.tests.components.ui;

import com.vaadin.server.ExternalResource;
import com.vaadin.server.Page;
import com.vaadin.server.Page.UriFragmentChangedEvent;
import com.vaadin.server.VaadinRequest;
@@ -7,6 +8,7 @@ import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Label;
import com.vaadin.ui.Link;

public class UriFragment extends AbstractReindeerTestUI {

@@ -29,6 +31,12 @@ public class UriFragment extends AbstractReindeerTestUI {
addComponent(createButton("test", "Navigate to #test", "test"));
addComponent(createButton("empty", "Navigate to #", ""));
addComponent(createButton("null", "setUriFragment(null)", null));

Link link = new Link("Navigate to #linktest",
new ExternalResource("#linktest"));
link.setId("link");
addComponent(link);

}

private Button createButton(String id, String caption,

+ 69
- 0
uitest/src/test/java/com/vaadin/tests/components/ui/PushStateAndReplaceStateTest.java 파일 보기

@@ -0,0 +1,69 @@
package com.vaadin.tests.components.ui;

import static org.junit.Assert.assertEquals;

import java.net.URI;

import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;

import com.vaadin.testbench.By;
import com.vaadin.tests.tb3.MultiBrowserTest;

public class PushStateAndReplaceStateTest extends MultiBrowserTest {

@Test
public void testUriFragment() throws Exception {
driver.get(getTestUrl());
assertUri(getTestUrl());

hitButton("test");

assertUri(getTestUrl() + "/test");

driver.navigate().back();

driver.findElement(By.className("v-Notification")).getText()
.contains("Popstate event");

assertUri(getTestUrl());

hitButton("test");
URI base = new URI(getTestUrl() + "/test");
hitButton("X");
URI current = base.resolve("X");
driver.findElement(By.xpath("//*[@id = 'replace']/input")).click();
hitButton("root_X");
current = current.resolve("/X");

assertUri(current.toString());

// Now that last change was with replace state, two back calls should go
// to initial
driver.navigate().back();
driver.navigate().back();

assertUri(getTestUrl());

}

private void assertUri(String uri) {
final String expectedText = "Current Location: " + uri;
waitUntil(new ExpectedCondition<Boolean>() {

@Override
public Boolean apply(WebDriver input) {
return expectedText.equals(getLocationLabelValue());
}
});

assertEquals(uri, driver.getCurrentUrl());
}

private String getLocationLabelValue() {
String text = vaadinElementById("locationLabel").getText();
return text;
}

}

+ 7
- 0
uitest/src/test/java/com/vaadin/tests/components/ui/UriFragmentTest.java 파일 보기

@@ -7,6 +7,7 @@ import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;

import com.vaadin.testbench.By;
import com.vaadin.tests.tb3.MultiBrowserTest;

public class UriFragmentTest extends MultiBrowserTest {
@@ -42,6 +43,12 @@ public class UriFragmentTest extends MultiBrowserTest {
navigateToNull(); // Setting to null when there is a fragment actually
// sets it to #
assertEquals("Current URI fragment:", getFragmentLabelValue());

// ensure IE works with new popstate based implementation, see
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/3740423/
driver.findElement(By.xpath("//*[@id = 'link']/a")).click();
assertFragment("linktest");

}

private void assertFragment(String fragment) {

+ 11
- 0
uitest/src/test/java/com/vaadin/tests/navigator/NavigatorViewBlocksBackButtonActionTest.java 파일 보기

@@ -17,15 +17,26 @@ package com.vaadin.tests.navigator;

import static org.junit.Assert.assertEquals;

import java.util.List;

import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;

import com.vaadin.testbench.elements.ButtonElement;
import com.vaadin.tests.tb3.MultiBrowserTest;

public class NavigatorViewBlocksBackButtonActionTest extends MultiBrowserTest {

@Override
public List<DesiredCapabilities> getBrowsersToTest() {
// IE web driver fails to read fragment properly, these must be tested
// manually. See
// https://github.com/SeleniumHQ/selenium-google-code-issue-archive/issues/7966
return getBrowsersExcludingIE();
}

@Test
public void testIfConfirmBack() {
openTestURL();

+ 11
- 0
uitest/src/test/java/com/vaadin/tests/urifragments/FragmentHandlingAndAsynchUIUpdateTest.java 파일 보기

@@ -18,9 +18,12 @@ package com.vaadin.tests.urifragments;
import static com.vaadin.tests.urifragments.FragmentHandlingAndAsynchUIUpdate.FRAG_NAME_TPL;
import static com.vaadin.tests.urifragments.FragmentHandlingAndAsynchUIUpdate.START_FRAG_ID;

import java.util.List;

import org.junit.Test;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedCondition;

import com.vaadin.testbench.By;
@@ -33,6 +36,14 @@ import com.vaadin.tests.tb3.MultiBrowserTest;
*/
public class FragmentHandlingAndAsynchUIUpdateTest extends MultiBrowserTest {

@Override
public List<DesiredCapabilities> getBrowsersToTest() {
// IE web driver fails to read fragment properly, these must be tested
// manually. See
// https://github.com/SeleniumHQ/selenium-google-code-issue-archive/issues/7966
return getBrowsersExcludingIE();
}

/**
* The case when we successively set 10 fragments, go back 9 times and then
* go forward 9 times

+ 11
- 0
uitest/src/test/java/com/vaadin/tests/urifragments/SettingNullFragmentTest.java 파일 보기

@@ -15,8 +15,11 @@
*/
package com.vaadin.tests.urifragments;

import java.util.List;

import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedCondition;

import com.vaadin.tests.tb3.MultiBrowserTest;
@@ -29,6 +32,14 @@ import com.vaadin.tests.tb3.MultiBrowserTest;
*/
public class SettingNullFragmentTest extends MultiBrowserTest {

@Override
public List<DesiredCapabilities> getBrowsersToTest() {
// IE web driver fails to read fragment properly, these must be tested
// manually. See
// https://github.com/SeleniumHQ/selenium-google-code-issue-archive/issues/7966
return getBrowsersExcludingIE();
}

@Test
public void testSettingNullURIFragment() throws Exception {
openTestURL();

Loading…
취소
저장