1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package test.javafx.scene.web; 27 28 import org.junit.Test; 29 import org.junit.Before; 30 31 import java.util.concurrent.CountDownLatch; 32 import java.util.concurrent.TimeUnit; 33 import java.util.concurrent.atomic.AtomicInteger; 34 35 import static org.junit.Assert.assertEquals; 36 import static org.junit.Assert.assertNotNull; 37 import static org.junit.Assert.assertNull; 38 import static org.junit.Assert.assertTrue; 39 import static org.junit.Assert.fail; 40 41 public class HistoryStateTest extends TestBase { 42 private static final CountDownLatch historyStateLatch = new CountDownLatch(3); 43 final AtomicInteger historyListenerIndex = new AtomicInteger(-1); 44 45 private static final String resourcePath= "test/html/"; 46 private static final String initialLoadUrl = "archive-root0.html"; 47 private static final String firstLoadUrl = "archive-root1.html"; 48 private static final String secondLoadUrl = "archive-root2.html"; 49 private static final String replaceLoadUrl = "archive-root3.html"; 50 51 private static final String historyPushScript1 = "history.pushState({push1key : 1}, '', '?" + 52 firstLoadUrl + "');"; 53 private static final String historyPushScript2 = "history.pushState({push2key : 2}, '', '?" + 54 secondLoadUrl + "');"; 55 private static final String historyReplaceScript = "history.replaceState({replaceObject : 3}, '', '?" + 56 replaceLoadUrl + "');"; 57 private static final String historyStateScript = "history.state"; 58 private static final String historyLengthScript = "history.length"; 59 private static final String historyGoBackScript = "history.go(-1)"; 60 private static final String historyGoForwardScript = "history.go(1)"; 61 private static final String historyBackcript = "history.back()"; 62 63 private static final int TIMEOUT = 30; // seconds 64 65 @Before 66 public void before() { 67 load(HistoryStateTest.class.getClassLoader().getResource( 68 resourcePath + initialLoadUrl).toExternalForm()); 69 } 70 71 @Test 72 public void pushAndReplaceTest() throws Exception { 73 // Initial history.state should be null 74 assertNull(historyStateScript + " : Failed", 75 executeScript(historyStateScript)); 76 // Initial history.length will be 1 77 assertEquals(historyLengthScript + " : Failed", 78 1, executeScript(historyLengthScript)); 79 80 // history.pushState({push1Key : 1}, '', '?firstLoadUrl"'); 81 executeScript(historyPushScript1); 82 // Check if the history.state object for not null 83 assertNotNull(historyStateScript + " : Failed", 84 executeScript(historyStateScript)); 85 // {push1Key : 1} : {key = push1Key :value = (Integer) 1} 86 assertEquals("history.state.push1key Failed", 87 1, executeScript("history.state.push1key")); 88 89 // history.length expected to be 2 90 // Initial load + history.pushState(...) 91 assertEquals(historyLengthScript + " : Failed", 92 2, executeScript(historyLengthScript)); 93 94 // Check for WebEngine location is updated with new URL 95 assertTrue(historyPushScript1 + " : Failed", 96 getEngine().getLocation().endsWith(firstLoadUrl)); 97 98 99 executeScript(historyPushScript2); 100 // {push2Key : 2} : {key = push1Key :value = (Integer) 2} 101 assertEquals("history.state.push1key Failed", 102 2, executeScript("history.state.push2key")); 103 104 // history.length expected to be 2 105 // Initial load + history.pushState(...) 106 assertEquals(historyLengthScript + " : Failed", 107 3, executeScript(historyLengthScript)); 108 109 // Check for WebEngine location is updated with new URL 110 assertTrue(historyPushScript2 + " : Failed", 111 getEngine().getLocation().endsWith(secondLoadUrl)); 112 113 executeScript(historyReplaceScript); 114 // history.length remains same 115 assertEquals(historyLengthScript + " : Failed", 116 3, executeScript(historyLengthScript)); 117 118 assertEquals("history.state.replaceObject Failed", 119 3, executeScript("history.state.replaceObject")); 120 121 // Check for WebEngine location is updated with new URL 122 assertTrue(historyPushScript2 + " : Failed", 123 getEngine().getLocation().endsWith(replaceLoadUrl)); 124 125 submit(() -> { 126 getEngine().locationProperty().addListener((observable, previousUrl, newUrl) -> { 127 switch(historyListenerIndex.incrementAndGet()) { 128 case 0: 129 // call back to history.go(-1) --> newUrl = initialLoadURL 130 assertTrue(newUrl.endsWith(firstLoadUrl)); 131 // history.go(1), navigate forward 132 getEngine().executeScript(historyGoForwardScript); 133 break; 134 case 1: 135 // call back to history.go(1) --> newURL = firstLoad 136 assertTrue(newUrl.endsWith(replaceLoadUrl)); 137 // navigate back using history.back() 138 getEngine().executeScript(historyBackcript); 139 break; 140 case 2: 141 // call back to history.back() --> newURL = initialLoadUrl 142 assertTrue(newUrl.endsWith(firstLoadUrl)); 143 break; 144 default: 145 fail(); 146 } 147 historyStateLatch.countDown(); 148 }); 149 // history.go(-1), location will update in listener asynchronously 150 // expected to go back to firstLoadUrl based on previous states 151 // a. history.pushState(,,firstLoadUrl) 152 // b. history.pushState(,,secondUrl) 153 // c. history.replaceState(,,replaceLoadUrl) 154 getEngine().executeScript(historyGoBackScript); 155 }); 156 try { 157 historyStateLatch.await(TIMEOUT, TimeUnit.SECONDS); 158 } catch (InterruptedException ex) { 159 throw new AssertionError(ex); 160 } finally { 161 assertEquals("history navigation using javascript failed", 2, historyListenerIndex.get()); 162 } 163 } 164 } 165