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