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.regexp;
16  
17  import org.htmlunit.SimpleWebTestCase;
18  import org.htmlunit.WebClient;
19  import org.htmlunit.corejs.javascript.Context;
20  import org.htmlunit.corejs.javascript.JavaScriptException;
21  import org.htmlunit.corejs.javascript.ScriptableObject;
22  import org.htmlunit.html.HtmlPage;
23  import org.htmlunit.javascript.HtmlUnitContextFactory;
24  import org.junit.jupiter.api.Test;
25  
26  /**
27   * Tests for {@link HtmlUnitRegExpProxy}.
28   *
29   * @author Marc Guillemot
30   * @author Ahmed Ashour
31   * @author Carsten Steul
32   */
33  public class RegExp2Test extends SimpleWebTestCase {
34  
35      private static final String STR = "(?:<script.*?>)((\\n|\\r|.)*?)(?:<\\/script>)";
36      private static final String BEGIN = "<div>bla</div>";
37      private static final String END = "foo\n<span>bla2</span>";
38      private static final String TEXT = BEGIN + "<script>var a = 123;</script>" + END;
39      private static final String EXPECTED = BEGIN + END;
40      private static final String SRC = "var re = new RegExp(str, 'img');\n"
41          + "var s = text.replace(re, '');\n"
42          + "if (s != expected)\n"
43          + "  throw 'Expected >' + expected + '< but got >' + s + '<';";
44  
45      private static final String SCRIPT_TEST_MATCH = "function arrayToString(_arr) {\n"
46              + "  if (_arr == null) return null;\n"
47              + "  var s = '[';\n"
48              + "  for (var i = 0; i < _arr.length; i++)\n"
49              + "  {\n"
50              + "    if (i != 0) s += ', ';\n"
51              + "    s += _arr[i];\n"
52              + "  }\n"
53              + "  s += ']';\n"
54              + "  return s;\n"
55              + "}\n"
56  
57              + "function assertArrEquals(actual, expected) {\n"
58              + "  if (expected == null) {\n"
59              + "    if (actual != null)\n"
60              + "      throw 'Expected >null< got >' + actual + '<';\n"
61              + "    else return;\n"
62              + "  }\n"
63              + "  var expectedStr = arrayToString(expected);\n"
64              + "  var actualStr = arrayToString(actual);\n"
65              + "  if (expectedStr != actualStr)\n"
66              + "    throw 'Expected >' + expectedStr + '< got >' + actualStr + '<';\n"
67              + "}\n"
68  
69              + "assertArrEquals('ab'.match(), ['']);\n"
70              + "assertArrEquals('ab'.match('foo'), null);\n"
71              + "assertArrEquals('ab'.match('a'), ['a']);\n"
72              + "assertArrEquals('abab'.match('a'), ['a']);\n"
73              + "assertArrEquals('abab'.match('.a'), ['ba']);\n"
74              + "assertArrEquals('abab'.match(/.a/), ['ba']);\n"
75              + "assertArrEquals('li'.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i), ['li', undefined, 'li', '']);\n"
76              + "assertArrEquals('abab'.match(new RegExp('.a')), ['ba']);\n"
77              + "var s = '<script>var a = 1;</' + 'script>';\n"
78              + "var re = '(?:<script.*?>)((\\n|\\r|.)*?)(?:<\\/script>)';\n"
79              + "assertArrEquals(s.match(re), [s, 'var a = 1;', ';']);\n";
80  
81      /**
82       * Test that string.replace works correctly (?) in HtmlUnit.
83       * @throws Exception if the test fails
84       */
85      @Test
86      public void fixedInHtmlUnit() throws Exception {
87          final String html = "<html></html>";
88          final HtmlPage page = loadPage(html);
89          final ScriptableObject topScope = page.getEnclosingWindow().getScriptableObject();
90          topScope.put("str", topScope, STR);
91          topScope.put("text", topScope, TEXT);
92          topScope.put("expected", topScope, EXPECTED);
93          page.executeJavaScript(SRC);
94      }
95  
96      /**
97       * Tests if custom patch is still needed.
98       */
99      @Test
100     public void needCustomFix() {
101         final WebClient client = getWebClient();
102         final HtmlUnitContextFactory cf = client.getJavaScriptEngine().getContextFactory();
103         final Context ctx = cf.enterContext();
104         try {
105             final ScriptableObject topScope = ctx.initStandardObjects();
106             topScope.put("str", topScope, STR);
107             topScope.put("text", topScope, TEXT);
108             topScope.put("expected", topScope, EXPECTED);
109             assertEquals(BEGIN + END, TEXT.replaceAll(STR, ""));
110             try {
111                 ctx.evaluateString(topScope, SRC, "test script", 0, null);
112             }
113             catch (final JavaScriptException e) {
114                 assertTrue(e.getMessage().indexOf("Expected >") == 0);
115             }
116         }
117         finally {
118             Context.exit();
119         }
120     }
121 
122     /**
123      * Test if the custom fix is needed or not. When this test fails, then it means that the problem is solved in
124      * Rhino and that custom fix for String.match in {@link HtmlUnitRegExpProxy} is not needed anymore (and that
125      * this test can be removed, or turned positive).
126      * @throws Exception if the test fails
127      */
128     @Test
129     public void matchFixNeeded() throws Exception {
130         final WebClient client = getWebClient();
131         final HtmlUnitContextFactory cf = client.getJavaScriptEngine().getContextFactory();
132         final Context cx = cf.enterContext();
133         try {
134             final ScriptableObject topScope = cx.initStandardObjects();
135             cx.evaluateString(topScope, SCRIPT_TEST_MATCH, "test script String.match", 0, null);
136             try {
137                 cx.evaluateString(topScope, SCRIPT_TEST_MATCH, "test script String.match", 0, null);
138             }
139             catch (final JavaScriptException e) {
140                 assertTrue(e.getMessage().indexOf("Expected >") == 0);
141             }
142         }
143         finally {
144             Context.exit();
145         }
146     }
147 
148     /**
149      * @throws Exception if the test fails
150      */
151     @Test
152     public void emptySubStringChanged() throws Exception {
153         final String html = "<html></html>";
154         final HtmlPage page = loadPage(html);
155         page.executeJavaScript("'alpha'.replace(/alpha/, '');/beta/.test('abc beta def');");
156     }
157 }