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.html;
16  
17  import org.htmlunit.SimpleWebTestCase;
18  import org.htmlunit.corejs.javascript.ScriptableObject;
19  import org.htmlunit.javascript.host.html.HTMLElement;
20  import org.htmlunit.junit.annotation.Alerts;
21  import org.htmlunit.junit.annotation.HtmlUnitNYI;
22  import org.junit.jupiter.api.BeforeEach;
23  import org.junit.jupiter.api.Test;
24  
25  /**
26   * Tests for {@link HtmlTableRow}.
27   *
28   * @author <a href="mailto:gallaherm@pragmatics.com">Mike Gallaher</a>
29   * @author Mike Bowler
30   * @author Ahmed Ashour
31   * @author Marc Guillemot
32   * @author Ronald Brill
33   */
34  public class HtmlTableRowTest extends SimpleWebTestCase {
35  
36      private static final String HTML = DOCTYPE_HTML
37              + "<html><head><title>foo</title></head><body>\n"
38              + "<table id='table'><tr id='row'>\n"
39              + "<td id='cell' width='20'><input type='text' id='foo'/></td>\n"
40              + "</tr></table>\n"
41              + "</body></html>";
42  
43      private HtmlPage page_;
44      private HtmlTable table_;
45      private HtmlTableBody tbody_;
46      private HtmlTableRow row_;
47      private HtmlTableCell cell_;
48      private HtmlTableRow rowClone_;
49      private HtmlTableCell cellClone_;
50  
51      /**
52       * Constructor.
53       * @throws Exception if an exception occurs
54       */
55      @BeforeEach
56      public void init() throws Exception {
57          page_ = loadPage(HTML);
58  
59          table_ = page_.getHtmlElementById("table");
60          tbody_ = (HtmlTableBody) table_.getFirstChild();
61          row_ = table_.getRow(0);
62          cell_ = row_.getCell(0);
63  
64          rowClone_ = (HtmlTableRow) row_.cloneNode(true);
65          cellClone_ = rowClone_.getCell(0);
66      }
67  
68      /**
69       * Ensure that cloneNode leaves the original node unchanged.
70       */
71      @Test
72      public void clonePreservesOriginal() {
73          assertSame(tbody_, row_.getParentNode());
74          assertSame(row_, cell_.getParentNode());
75          assertSame(cell_, row_.getCell(0));
76          assertEquals("row", row_.getId());
77          assertEquals("cell", cell_.getId());
78      }
79  
80      /**
81       * Ensure that the clones are not the originals.
82       * @throws Exception if the test fails
83       */
84      @Test
85      public void clonesAreDistinct() throws Exception {
86          assertNotSame(row_, rowClone_);
87          assertNotSame(cell_, cellClone_);
88      }
89  
90      /**
91       * Ensure that the clones have the same page as the originals.
92       */
93      @Test
94      public void cloneHasSamePage() {
95          assertSame(cell_.getPage(), cellClone_.getPage());
96          assertSame(row_.getPage(), rowClone_.getPage());
97      }
98  
99      /**
100      * Ensure that the cloned row has no parent.
101      * @throws Exception if the test fails
102      */
103     @Test
104     public void clonedRowHasNullParent() throws Exception {
105         assertNull(rowClone_.getParentNode());
106     }
107 
108     /**
109      * Ensure that the cloned row's children are not those of the original.
110      * @throws Exception if the test fails
111      */
112     @Test
113     public void clonedRowHasDifferentChildren() throws Exception {
114         assertEquals(row_.getCells().size(), rowClone_.getCells().size());
115         assertNotSame(row_.getFirstChild(), rowClone_.getFirstChild());
116     }
117 
118     /**
119      * Ensure that the cloned cell's children are not those of the original.
120      */
121     @Test
122     public void clonedCellHasDifferentChildren() {
123         assertNotSame(cell_.getParentNode(), cellClone_.getParentNode());
124         assertNotNull(cell_.getFirstChild());
125         assertNotSame(cell_.getFirstChild(), cellClone_.getFirstChild());
126     }
127 
128     /**
129      * Ensure that the cloned cell has the cloned row as its parent.
130      * @throws Exception if the test fails
131      */
132     @Test
133     public void clonedCellHasClonedRowAsParent() throws Exception {
134         assertSame(rowClone_, cellClone_.getParentNode());
135     }
136 
137     /**
138      * Ensure the cloned cell's attribute value is the same as the original.
139      */
140     @Test
141     public void cloneAttributesCopiedFromOriginal() {
142         assertEquals("20", cell_.getAttribute("width"));
143         assertEquals("20", cellClone_.getAttribute("width"));
144     }
145 
146     /**
147      * Ensure that changing the clone's attribute leaves the original's
148      * attribute unchanged.
149      */
150     @Test
151     public void cloneAttributeIsIndependentOfOriginal() {
152         cellClone_.setAttribute("a", "one");
153         assertFalse("one".equals(cell_.getAttribute("a")));
154     }
155 
156     /**
157      * Ensure that changing the original's attribute leaves the clone's
158      * attribute unchanged.
159      */
160     @Test
161     public void originalAttributeIsIndependentOfClone() {
162         cell_.setAttribute("a", "one");
163         assertFalse("one".equals(cellClone_.getAttribute("a")));
164     }
165 
166     /**
167      * Ensure that changing the clone's nodeValue leaves the original's
168      * unchanged.
169      */
170     @Test
171     public void cloneValueIsIndependentOfOriginal() {
172         cellClone_.setNodeValue("one");
173         assertFalse("one".equals(cell_.getNodeValue()));
174     }
175 
176     /**
177      * Ensure that changing the clone's id leaves the original's unchanged.
178      */
179     @Test
180     public void cloneIdIsIndependentOfOriginal() {
181         cellClone_.setNodeValue("one");
182         assertFalse("one".equals(cell_.getNodeValue()));
183     }
184 
185     // these next few test our assumptions about how scripts affect the DOM
186 
187     /**
188      * Ensure that the JavaScript object returned by the script fragment really
189      * refers to the same DOM node we think it should.
190      */
191     @Test
192     public void scriptCanGetOriginalCell() {
193         final String cmd = "document.getElementById('cell')";
194         final Object object = page_.executeJavaScript(cmd).getJavaScriptResult();
195 
196         final HtmlElement cellElement = ((HTMLElement) object).getDomNodeOrDie();
197         assertSame(cell_, cellElement);
198     }
199 
200     /**
201      * Ensure that the JavaScript object returned by the script fragment is the
202      * same one the DOM node thinks it's wrapped by.
203      */
204     @Test
205     public void cellScriptObjectIsReturnedByScript() {
206         final String cmd = "document.getElementById('cell')";
207         final HTMLElement jselement = (HTMLElement) page_.executeJavaScript(cmd).getJavaScriptResult();
208 
209         assertSame(jselement, cell_.getScriptableObject());
210     }
211 
212     /**
213      * Ensure that setting a property via script sets the property on the
214      * ScriptableObject that we think it should.
215      */
216     @Test
217     public void scriptCanSetJsPropertyOnCell() {
218         final String cmd = "document.getElementById('cell').a = 'original'; document.getElementById('cell')";
219         final Object object = page_.executeJavaScript(cmd).getJavaScriptResult();
220 
221         final HTMLElement jselement = (HTMLElement) object;
222         assertEquals("original", ScriptableObject.getProperty(jselement, "a"));
223 
224         assertSame(jselement, cell_.getScriptableObject());
225     }
226 
227     /**
228      * Ensure that a script can set the disabled property on a DOM node.
229      */
230     @Test
231     @Alerts("disabled")
232     @HtmlUnitNYI(CHROME = "",
233             EDGE = "",
234             FF = "",
235             FF_ESR = "")
236     public void cloneScriptCanSetDisabledOnCell() {
237         final String cmd = "document.getElementById('cell').disabled = 'true'";
238         page_.executeJavaScript(cmd);
239 
240         assertEquals(getExpectedAlerts()[0], cell_.getAttribute("disabled"));
241     }
242 
243     /**
244      * Ensure that a script can set an attribute on the DOM node.
245      */
246     @Test
247     public void cloneScriptCanSetAttributeOnCell() {
248         final String cmd = "document.getElementById('cell').setAttribute('a','original')";
249         page_.executeJavaScript(cmd);
250         assertEquals("original", cell_.getAttribute("a"));
251     }
252 
253     // these next few check that scripts manipulate the clone independently
254 
255     /**
256      * Ensure that a script setting an attribute on the original does not affect
257      * that same attribute on the clone.
258      */
259     @Test
260     public void cloneScriptSetAttributeIndependentOfOriginal() {
261         final String cmd = "document.getElementById('cell').setAttribute('a','original')";
262         page_.executeJavaScript(cmd);
263 
264         assertEquals("original", cell_.getAttribute("a"));
265         assertFalse("original".equals(cellClone_.getAttribute("a")));
266     }
267 
268     /**
269      * Ensure that a script setting disabled on the original does not affect
270      * that same attribute on the clone.
271      */
272     @Test
273     @Alerts({"disabled", ""})
274     @HtmlUnitNYI(CHROME = {"", ""},
275             EDGE = {"", ""},
276             FF = {"", ""},
277             FF_ESR = {"", ""})
278     public void cloneScriptSetDisabledIndependentOfOriginal() {
279         final String cmd = "document.getElementById('cell').disabled = 'true'";
280         page_.executeJavaScript(cmd);
281 
282         assertEquals(getExpectedAlerts()[0], cell_.getAttribute("disabled"));
283         assertEquals(getExpectedAlerts()[1], cellClone_.getAttribute("disabled"));
284     }
285 
286     /**
287      * Ensure that a script referencing an element causes only that DOM element
288      * to get a ScriptObject assigned, and does not cause one to be assigned to
289      * the clone.
290      */
291     @Test
292     public void cloneHasDifferentScriptableObject() {
293         final String cmd = "document.getElementById('cell')"; // force it to have a
294         // scriptable object
295         page_.executeJavaScript(cmd);
296 
297         assertNotSame(cell_.getScriptableObject(), cellClone_.getScriptableObject());
298     }
299 
300     /**
301      * Ensure that setting the value on a child of a table cell doesn't affect
302      * the cloned child.
303      */
304     @Test
305     public void scriptDomOperations() {
306         final String cmd = "document.getElementById('foo').value = 'Input!';document.getElementById('foo')";
307         page_.executeJavaScript(cmd);
308 
309         final HtmlInput input = (HtmlInput) cell_.getFirstChild();
310         assertEquals("", input.getValueAttribute());
311         assertEquals("Input!", input.getValue());
312 
313         final HtmlInput inputClone = (HtmlInput) cellClone_.getFirstChild();
314         assertEquals("", inputClone.getValueAttribute());
315         assertFalse("Input!".equals(inputClone.getValue()));
316     }
317 }