View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.javascript.host;
16  
17  import org.htmlunit.WebDriverTestCase;
18  import org.htmlunit.junit.BrowserRunner;
19  import org.htmlunit.junit.annotation.Alerts;
20  import org.htmlunit.junit.annotation.HtmlUnitNYI;
21  import org.junit.Test;
22  import org.junit.runner.RunWith;
23  import org.openqa.selenium.WebDriver;
24  import org.openqa.selenium.htmlunit.HtmlUnitDriver;
25  
26  /**
27   * Tests for {@link Storage}.
28   *
29   * @author Ahmed Ashour
30   * @author Marc Guillemot
31   * @author Frank Danek
32   * @author Jake Cobb
33   * @author Ronald Brill
34   */
35  @RunWith(BrowserRunner.class)
36  public class StorageTest extends WebDriverTestCase {
37  
38      /**
39       * @throws Exception if the test fails
40       */
41      @Test
42      @Alerts({"[object Storage]", "[object Storage]"})
43      public void storage() throws Exception {
44          final String html = DOCTYPE_HTML
45              + "<html><head></head><body>\n"
46              + "<script>\n"
47              + LOG_TITLE_FUNCTION
48              + "  log(window.localStorage);\n"
49              + "  log(window.sessionStorage);\n"
50              + "</script>\n"
51              + "</body></html>";
52          loadPageVerifyTitle2(html);
53      }
54  
55      /**
56       * @throws Exception if the test fails
57       */
58      @Test
59      @Alerts({"local: true", "session: true"})
60      public void storageEquals() throws Exception {
61          final String html = DOCTYPE_HTML
62              + "<html><body>\n"
63              + "<script>\n"
64              + LOG_TITLE_FUNCTION
65              + "  try { log('local: ' + (window.localStorage === window.localStorage)); }"
66                          + " catch(e) { logEx(e); }\n"
67              + "  try { log('session: ' + (window.sessionStorage === window.sessionStorage)); }"
68                          + " catch(e) { logEx(e); }\n"
69              + "</script></body></html>";
70          loadPageVerifyTitle2(html);
71      }
72  
73      /**
74       * @throws Exception if the test fails
75       */
76      @Test
77      @Alerts({"string", "1"})
78      public void localStorage() throws Exception {
79          final String firstHtml = DOCTYPE_HTML
80              + "<html><head></head><body>\n"
81              + "<script>\n"
82              + "  if (window.localStorage) {\n"
83              + "    localStorage.hello = 1;\n"
84              + "  }\n"
85              + "</script>\n"
86              + "</body></html>";
87          final String secondHtml = DOCTYPE_HTML
88              + "<html><head></head><body>\n"
89              + "<script>\n"
90              + LOG_TITLE_FUNCTION
91              + "  if (window.localStorage) {\n"
92              + "    log(typeof localStorage.hello);\n"
93              + "    log(localStorage.hello);\n"
94              + "  }\n"
95              + "</script>\n"
96              + "</body></html>";
97          loadPage2(firstHtml);
98          getMockWebConnection().setResponse(URL_SECOND, secondHtml);
99  
100         final WebDriver driver = getWebDriver();
101         driver.get(URL_SECOND.toExternalForm());
102         verifyTitle2(driver, getExpectedAlerts());
103     }
104 
105     /**
106      * @throws Exception if the test fails
107      */
108     @Test
109     @Alerts({"works 5200000", "fails 5290000"})
110     public void localStorageSizeOneEntry() throws Exception {
111         final String firstHtml = DOCTYPE_HTML
112             + "<html>\n"
113             + "<body>\n"
114             + "<script>\n"
115             + LOG_TITLE_FUNCTION
116             + "  if (window.localStorage) {\n"
117             + "    localStorage.clear();\n"
118 
119             + "    var content = '';"
120             + "    for (var i = 0; i < 10000; i++) { content += '0123456789' }"
121             + "    var bigContent = '';\n"
122 
123             + "    for (var i = 0; i < 52; i++) {\n"
124             + "      bigContent += content;\n"
125             + "    }"
126 
127             + "    try {"
128             + "      localStorage.setItem('HtmlUnit', bigContent);\n"
129             + "      log('works ' + bigContent.length);\n"
130             + "    } catch(e) {\n"
131             + "      log('fails ' + bigContent.length);\n"
132             + "    }\n"
133 
134             + "    content = '';"
135             + "    for (var i = 0; i < 9000; i++) { content += '0123456789' }"
136             + "    bigContent += content;\n"
137             + "    try {"
138             + "      localStorage.setItem('HtmlUnit', bigContent);\n"
139             + "      log('works ' + bigContent.length);\n"
140             + "    } catch(e) {\n"
141             + "      log('fails ' + bigContent.length);\n"
142             + "    }\n"
143             + "  }\n"
144             + "</script>\n"
145             + "</body></html>";
146         loadPageVerifyTitle2(firstHtml);
147     }
148 
149     /**
150      * @throws Exception if the test fails
151      */
152     @Test
153     @Alerts({"works 52", "fails"})
154     public void localStorageSizeManyEntries() throws Exception {
155         final String firstHtml = DOCTYPE_HTML
156             + "<html>\n"
157             + "<body>\n"
158             + "<script>\n"
159             + LOG_TITLE_FUNCTION
160             + "  if (window.localStorage) {\n"
161             + "    localStorage.clear();\n"
162 
163             + "    var content = '';"
164             + "    for (var i = 0; i < 10000; i++) { content += '0123456789' }"
165             + "    var bigContent = content;\n"
166 
167             + "    for (var i = 0; i < 52; i++) {\n"
168             + "      try {"
169             + "        localStorage.setItem('HtmlUnit-' + i, bigContent);\n"
170             + "      } catch(e) {\n"
171             + "        log('fails ' + i);\n"
172             + "        break;\n"
173             + "      }\n"
174             + "    }"
175             + "    log('works ' + i);\n"
176 
177             + "    try {"
178             + "      localStorage.setItem('HtmlUnit-xx', bigContent);\n"
179             + "      log('works');\n"
180             + "    } catch(e) {\n"
181             + "      log('fails');\n"
182             + "    }\n"
183             + "  }\n"
184             + "</script>\n"
185             + "</body></html>";
186         loadPageVerifyTitle2(firstHtml);
187     }
188 
189     /**
190      * @throws Exception if the test fails
191      */
192     @Test
193     @Alerts({"true", "true", "null", "null"})
194     public void localStorageKey() throws Exception {
195         final String html = DOCTYPE_HTML
196             + "<html>\n"
197             + "<body>\n"
198             + "<script>\n"
199             + LOG_TITLE_FUNCTION
200             + "  if (window.localStorage) {\n"
201             + "    localStorage.clear();\n"
202 
203             + "    localStorage.setItem('HtmlUnit', '0');\n"
204             + "    localStorage.setItem('HtmlUnit 1', '1');\n"
205             + "    localStorage.setItem('HtmlUnit 2', '2');\n"
206 
207             + "    log(localStorage.key(0).startsWith('HtmlUnit'));\n"
208             + "    log(localStorage.key(1).startsWith('HtmlUnit'));\n"
209             + "    log(localStorage.key(3));\n"
210             + "    log(localStorage.key(-1));\n"
211             + "  }\n"
212             + "</script>\n"
213             + "</body></html>";
214         loadPageVerifyTitle2(html);
215     }
216 
217     /**
218      * @throws Exception if the test fails
219      */
220     @Test
221     @Alerts({"0", "2", "there", "world", "1", "0"})
222     public void sessionStorage() throws Exception {
223         final String html = DOCTYPE_HTML
224             + "<html><head></head><body>\n"
225             + "<script>\n"
226             + LOG_TITLE_FUNCTION
227             + "  if (window.sessionStorage) {\n"
228             + "    log(sessionStorage.length);\n"
229             + "    sessionStorage.hi = 'there';\n"
230             + "    sessionStorage.setItem('hello', 'world');\n"
231             + "    log(sessionStorage.length);\n"
232             + "    log(sessionStorage.getItem('hi'));\n"
233             + "    log(sessionStorage.getItem('hello'));\n"
234             + "    sessionStorage.removeItem(sessionStorage.key(0));\n"
235             + "    log(sessionStorage.length);\n"
236             + "    if (sessionStorage.clear) {\n"
237             + "      sessionStorage.clear();\n"
238             + "      log(sessionStorage.length);\n"
239             + "    }\n"
240             + "  }\n"
241             + "</script>\n"
242             + "</body></html>";
243         loadPageVerifyTitle2(html);
244     }
245 
246     /**
247      * Note that this test will work only with WebDriver instances that support starting 2 instances in parallel.
248      * @throws Exception if the test fails
249      */
250     @Test
251     @Alerts({"", "I was here§"})
252     @HtmlUnitNYI(CHROME = {"", "null§"},
253             EDGE = {"", "null§"},
254             FF = {"", "null§"},
255             FF_ESR = {"", "null§"})
256     // TODO somehow persist the LocalStorage
257     public void localStorageShouldBeShared() throws Exception {
258         final String html1 = DOCTYPE_HTML
259             + "<html><body>\n"
260             + "<script>\n"
261             + LOG_TITLE_FUNCTION
262             + "try {\n"
263             + "  localStorage.clear();\n"
264             + "  localStorage.setItem('hello', 'I was here');\n"
265             + "} catch(e) { logEx(e); }\n"
266             + "</script></body></html>";
267 
268         WebDriver driver = loadPage2(html1);
269         assertEquals(getExpectedAlerts()[0], driver.getTitle());
270         releaseResources();
271 
272         final String html2 = DOCTYPE_HTML
273             + "<html><body><script>\n"
274             + LOG_TITLE_FUNCTION
275             + "try {\n"
276             + "  log(localStorage.getItem('hello'));\n"
277             + "} catch(e) { logEx(e); }\n"
278             + "</script></body></html>";
279         getMockWebConnection().setResponse(URL_FIRST, html2);
280 
281         // we have to control 2nd driver by ourself
282         driver = loadPage2(html2);
283         assertEquals(getExpectedAlerts()[1], driver.getTitle());
284 
285         if (!(driver instanceof HtmlUnitDriver)) {
286             shutDownAll();
287         }
288     }
289 
290     /**
291      * @throws Exception if the test fails
292      */
293     @Test
294     @Alerts({"undefined", "null", "extraMethod called", "null"})
295     public void prototypeIsExtensible() throws Exception {
296         final String html = DOCTYPE_HTML
297             + "<html><body>\n"
298             + "<script>\n"
299             + LOG_TITLE_FUNCTION
300             + "try {\n"
301             + "  localStorage.clear();\n"
302             + "  log(localStorage.extraMethod);\n"
303             + "  log(localStorage.getItem('extraMethod'));\n"
304             + "  Storage.prototype.extraMethod = function() {\n"
305             + "    log('extraMethod called');\n"
306             + "  };\n"
307             + "  try {\n"
308             + "    localStorage.extraMethod();\n"
309             + "  } catch (e2) {\n"
310             + "    log('localStorage.extraMethod not callable');\n"
311             + "  }\n"
312             + "  log(localStorage.getItem('extraMethod'));\n"
313             + "} catch(e) { logEx(e); }\n"
314             + "</script></body></html>";
315         loadPageVerifyTitle2(html);
316     }
317 
318     /**
319      * @throws Exception if the test fails
320      */
321     @Test
322     @Alerts({"function", "null", "function", "value", "1"})
323     public void prototypePropertiesAreVisible() throws Exception {
324         final String html = DOCTYPE_HTML
325             + "<html><body>\n"
326             + "<script>\n"
327             + LOG_TITLE_FUNCTION
328             + "try {\n"
329             + "  localStorage.clear();\n"
330             + "  log(typeof localStorage.hasOwnProperty);\n"
331             + "  log(localStorage.getItem('hasOwnProperty'));\n"
332             + "  localStorage.setItem('hasOwnProperty', 'value');\n"
333             + "  log(typeof localStorage.hasOwnProperty);\n"
334             + "  log(localStorage.getItem('hasOwnProperty'));\n"
335             + "} catch(e) { logEx(e); }\n"
336             + "  log(localStorage.length);\n"
337             + "</script></body></html>";
338         loadPageVerifyTitle2(html);
339     }
340 
341     /**
342      * @throws Exception if the test fails
343      */
344     @Test
345     @Alerts(DEFAULT = {"function", "null", "string", "null", "0"},
346             FF = {"function", "null", "function", "value", "1"},
347             FF_ESR = {"function", "null", "function", "value", "1"})
348     @HtmlUnitNYI(FF = {"function", "null", "string", "value", "1"},
349             FF_ESR = {"function", "null", "string", "value", "1"})
350     public void writeToPrototypeProperty() throws Exception {
351         final String html = DOCTYPE_HTML
352             + "<html><body>\n"
353             + "<script>\n"
354             + LOG_TITLE_FUNCTION
355             + "try {\n"
356             + "  localStorage.clear();\n"
357             + "  log(typeof localStorage.hasOwnProperty);\n"
358             + "  log(localStorage.getItem('hasOwnProperty'));\n"
359             + "  localStorage.hasOwnProperty = 'value';\n"
360             + "  log(typeof localStorage.hasOwnProperty);\n"
361             + "  log(localStorage.getItem('hasOwnProperty'));\n"
362             + "  log(localStorage.length);\n"
363             + "} catch(e) { logEx(e); }\n"
364             + "</script></body></html>";
365         loadPageVerifyTitle2(html);
366     }
367 }