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 java.util.ArrayList;
18  import java.util.List;
19  
20  import org.htmlunit.SimpleWebTestCase;
21  import org.junit.jupiter.api.Test;
22  
23  /**
24   * Tests for {@link HtmlTable}.
25   *
26   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
27   * @author Ahmed Ashour
28   * @author Marc Guillemot
29   * @author Ronald Brill
30   */
31  public class HtmlTableTest extends SimpleWebTestCase {
32  
33      /**
34       * Tests getTableCell(int,int).
35       * @exception Exception If the test fails
36       */
37      @Test
38      public void getCellAt() throws Exception {
39          final String htmlContent = DOCTYPE_HTML
40              + "<html><head><title>foo</title></head><body>\n"
41              + "<table id='table1' summary='Test table'>\n"
42              + "<tr><td>cell1</td><td>cell2</td><td rowspan='2'>cell4</td></tr>\n"
43              + "<tr><td colspan='2'>cell3</td></tr>\n"
44              + "</table>\n"
45              + "</body></html>";
46          final HtmlPage page = loadPage(htmlContent);
47  
48          final HtmlTable table = page.getHtmlElementById("table1");
49  
50          final HtmlTableCell cell1 = table.getCellAt(0, 0);
51          assertEquals("cell1 contents", "cell1", cell1.asNormalizedText());
52  
53          final HtmlTableCell cell2 = table.getCellAt(0, 1);
54          assertEquals("cell2 contents", "cell2", cell2.asNormalizedText());
55  
56          final HtmlTableCell cell3 = table.getCellAt(1, 0);
57          assertEquals("cell3 contents", "cell3", cell3.asNormalizedText());
58          assertSame("cells (1,0) and (1,1)", cell3, table.getCellAt(1, 1));
59  
60          final HtmlTableCell cell4 = table.getCellAt(0, 2);
61          assertEquals("cell4 contents", "cell4", cell4.asNormalizedText());
62          assertSame("cells (0,2) and (1,2)", cell4, table.getCellAt(1, 2));
63      }
64  
65      /**
66       * Tests getCellAt(int,int) with colspan.
67       * @exception Exception If the test fails
68       */
69      @Test
70      public void getCellAtColspan() throws Exception {
71          final String htmlContent = DOCTYPE_HTML
72              + "<html><head><title>foo</title></head><body>\n"
73              + "<table id='table1'>\n"
74              + "<tr>\n"
75              + "  <td>row 1 col 1</td>\n"
76              + "</tr>\n"
77              + "<tr>\n"
78              + "  <td>row 2 col 1</td><td>row 2 col 2</td>\n"
79              + "</tr>\n"
80              + "<tr>\n"
81              + "  <td colspan='1'>row 3 col 1&2</td>\n"
82              + "</tr>\n"
83              + "</table>\n"
84              + "</body></html>";
85          final HtmlPage page = loadPage(htmlContent);
86  
87          final HtmlTable table = page.getHtmlElementById("table1");
88  
89          final HtmlTableCell cell1 = table.getCellAt(0, 0);
90          assertEquals("cell (0,0) contents", "row 1 col 1", cell1.asNormalizedText());
91  
92          final HtmlTableCell cell2 = table.getCellAt(0, 1);
93          assertNull("cell (0,1) contents", cell2);
94  
95          final HtmlTableCell cell3 = table.getCellAt(1, 0);
96          assertEquals("cell (1,0) contents", "row 2 col 1", cell3.asNormalizedText());
97  
98          final HtmlTableCell cell4 = table.getCellAt(1, 1);
99          assertEquals("cell (1,1) contents", "row 2 col 2", cell4.asNormalizedText());
100 
101         final HtmlTableCell cell5 = table.getCellAt(2, 0);
102         assertEquals("cell (2, 0) contents", "row 3 col 1&2", cell5.asNormalizedText());
103         final HtmlTableCell cell6 = table.getCellAt(2, 1);
104         assertNull("cell (2, 1) contents", cell6);
105     }
106 
107     /**
108      * Tests getCellAt(int,int).
109      * @exception Exception If the test fails
110      */
111     @Test
112     public void getCellAtComplex() throws Exception {
113         final String htmlContent = DOCTYPE_HTML
114             + "<html><head><title>foo</title></head><body>\n"
115             + "<table id='table1' border='1'>\n"
116             + "  <tr>\n"
117             + "    <th colspan='1'>H 1.1</th><th>H 1.2</th>\n"
118             + "    <th colspan='2' rowspan='2'>H 1.3</th><th>H 1.5</th>\n"
119             + "  </tr>\n"
120             + "  <tr>\n"
121             + "    <th>H 2.1</th><th>H 2.2</th><th>H 2.5</th>\n"
122             + "  </tr>\n"
123             + "  <tr>\n"
124             + "    <td rowspan='3'>1.1</td><td colspan='3'>1.2</td><td>1.5</td>\n"
125             + "  </tr>\n"
126             + "  <tr>\n"
127             + "    <td rowspan='2'>2.2</td><td>2.3</td><td rowspan='4' colspan='2'>2.4</td>\n"
128             + "  </tr>\n"
129             + "  <tr>\n"
130             + "    <td>3.3</td>\n"
131             + "  </tr>\n"
132             + "  <tr>\n"
133             + "    <td>4.1</td><td>4.2</td><td>4.3</td>\n"
134             + "  </tr>\n"
135             + "  <tr>\n"
136             + "    <td>5.1</td>\n"
137             + "    <td colspan='2' rowspan='2'>5.2</td>\n"
138             + "  </tr>\n"
139             + "  <tr>\n"
140             + "    <td>6.1</td><td>6.4</td><td>6.5</td>\n"
141             + "  </tr>\n"
142             + "</table>\n"
143             + "</body></html>";
144         final HtmlPage page = loadPage(htmlContent);
145 
146         final HtmlTable table = page.getHtmlElementById("table1");
147 
148         // first row
149         HtmlTableCell cell = table.getCellAt(0, 0);
150         assertEquals("cell (0,0) contents", "H 1.1", cell.asNormalizedText());
151         cell = table.getCellAt(0, 1);
152         assertEquals("cell (0,1) contents", "H 1.2", cell.asNormalizedText());
153         cell = table.getCellAt(0, 2);
154         assertEquals("cell (0,2) contents", "H 1.3", cell.asNormalizedText());
155         cell = table.getCellAt(0, 3);
156         assertEquals("cell (0,3) contents", "H 1.3", cell.asNormalizedText());
157         cell = table.getCellAt(0, 4);
158         assertEquals("cell (0,4) contents", "H 1.5", cell.asNormalizedText());
159         cell = table.getCellAt(0, 5);
160         assertNull("cell (0,5) contents", cell);
161 
162         // second row
163         cell = table.getCellAt(1, 0);
164         assertEquals("cell (1,0) contents", "H 2.1", cell.asNormalizedText());
165         cell = table.getCellAt(1, 1);
166         assertEquals("cell (1,1) contents", "H 2.2", cell.asNormalizedText());
167         cell = table.getCellAt(1, 2);
168         assertEquals("cell (1,2) contents", "H 1.3", cell.asNormalizedText());
169         cell = table.getCellAt(1, 3);
170         assertEquals("cell (1,3) contents", "H 1.3", cell.asNormalizedText());
171         cell = table.getCellAt(1, 4);
172         assertEquals("cell (1,4) contents", "H 2.5", cell.asNormalizedText());
173         cell = table.getCellAt(1, 5);
174         assertNull("cell (0,5) contents", cell);
175 
176         // third row
177         cell = table.getCellAt(2, 0);
178         assertEquals("cell (2,0) contents", "1.1", cell.asNormalizedText());
179         cell = table.getCellAt(2, 1);
180         assertEquals("cell (2,1) contents", "1.2", cell.asNormalizedText());
181         cell = table.getCellAt(2, 2);
182         assertEquals("cell (2,2) contents", "1.2", cell.asNormalizedText());
183         cell = table.getCellAt(2, 3);
184         assertEquals("cell (2,3) contents", "1.2", cell.asNormalizedText());
185         cell = table.getCellAt(2, 4);
186         assertEquals("cell (2,4) contents", "1.5", cell.asNormalizedText());
187         cell = table.getCellAt(2, 5);
188         assertNull("cell (2,5) contents", cell);
189 
190         // fourth row
191         cell = table.getCellAt(3, 0);
192         assertEquals("cell (3,0) contents", "1.1", cell.asNormalizedText());
193         cell = table.getCellAt(3, 1);
194         assertEquals("cell (3,1) contents", "2.2", cell.asNormalizedText());
195         cell = table.getCellAt(3, 2);
196         assertEquals("cell (3,2) contents", "2.3", cell.asNormalizedText());
197         cell = table.getCellAt(3, 3);
198         assertEquals("cell (3,3) contents", "2.4", cell.asNormalizedText());
199         cell = table.getCellAt(3, 4);
200         assertEquals("cell (3,4) contents", "2.4", cell.asNormalizedText());
201         cell = table.getCellAt(3, 5);
202         assertNull("cell (3,5) contents", cell);
203 
204         // fifth row
205         cell = table.getCellAt(4, 0);
206         assertEquals("cell (4,0) contents", "1.1", cell.asNormalizedText());
207         cell = table.getCellAt(4, 1);
208         assertEquals("cell (4,1) contents", "2.2", cell.asNormalizedText());
209         cell = table.getCellAt(4, 2);
210         assertEquals("cell (4,2) contents", "3.3", cell.asNormalizedText());
211         cell = table.getCellAt(4, 3);
212         assertEquals("cell (4,3) contents", "2.4", cell.asNormalizedText());
213         cell = table.getCellAt(4, 4);
214         assertEquals("cell (4,4) contents", "2.4", cell.asNormalizedText());
215         cell = table.getCellAt(4, 5);
216         assertNull("cell (4,5) contents", cell);
217 
218         // sixth row
219         cell = table.getCellAt(5, 0);
220         assertEquals("cell (5,0) contents", "4.1", cell.asNormalizedText());
221         cell = table.getCellAt(5, 1);
222         assertEquals("cell (5,1) contents", "4.2", cell.asNormalizedText());
223         cell = table.getCellAt(5, 2);
224         assertEquals("cell (5,2) contents", "4.3", cell.asNormalizedText());
225         cell = table.getCellAt(5, 3);
226         assertEquals("cell (5,3) contents", "2.4", cell.asNormalizedText());
227         cell = table.getCellAt(5, 4);
228         assertEquals("cell (5,4) contents", "2.4", cell.asNormalizedText());
229         cell = table.getCellAt(5, 5);
230         assertNull("cell (5,5) contents", cell);
231 
232         // seventh row
233         cell = table.getCellAt(6, 0);
234         assertEquals("cell (6,0) contents", "5.1", cell.asNormalizedText());
235         cell = table.getCellAt(6, 1);
236         assertEquals("cell (6,1) contents", "5.2", cell.asNormalizedText());
237         cell = table.getCellAt(6, 2);
238         assertEquals("cell (6,2) contents", "5.2", cell.asNormalizedText());
239         cell = table.getCellAt(6, 3);
240         assertEquals("cell (6,3) contents", "2.4", cell.asNormalizedText());
241         cell = table.getCellAt(6, 4);
242         assertEquals("cell (6,4) contents", "2.4", cell.asNormalizedText());
243         cell = table.getCellAt(6, 5);
244         assertNull("cell (6,5) contents", cell);
245 
246         // eighth row
247         cell = table.getCellAt(7, 0);
248         assertEquals("cell (7,0) contents", "6.1", cell.asNormalizedText());
249         cell = table.getCellAt(7, 1);
250         assertEquals("cell (7,1) contents", "5.2", cell.asNormalizedText());
251         cell = table.getCellAt(7, 2);
252         assertEquals("cell (7,2) contents", "5.2", cell.asNormalizedText());
253         cell = table.getCellAt(7, 3);
254         assertEquals("cell (7,3) contents", "6.4", cell.asNormalizedText());
255         cell = table.getCellAt(7, 4);
256         assertEquals("cell (7,4) contents", "6.5", cell.asNormalizedText());
257         cell = table.getCellAt(7, 5);
258         assertNull("cell (6,5) contents", cell);
259 
260         // after the table
261         cell = table.getCellAt(8, 0);
262         assertNull("cell (8,0) contents", cell);
263     }
264 
265     /**
266      * Tests getTableCell(int,int) for a cell that doesn't exist.
267      * @exception Exception If the test fails
268      */
269     @Test
270     public void getTableCell_NotFound() throws Exception {
271         final String htmlContent = DOCTYPE_HTML
272             + "<html><head><title>foo</title></head><body>\n"
273             + "<table id='table1' summary='Test table'>\n"
274             + "<tr><td>cell1</td><td>cell2</td><td rowspan='2'>cell4</td></tr>\n"
275             + "<tr><td colspan='2'>cell3</td></tr>\n"
276             + "</table>\n"
277             + "</body></html>";
278         final HtmlPage page = loadPage(htmlContent);
279 
280         final HtmlTable table = page.getHtmlElementById("table1");
281 
282         final HtmlTableCell cell = table.getCellAt(99, 0);
283         assertNull("cell", cell);
284     }
285 
286     /**
287      * @throws Exception if the test fails
288      */
289     @Test
290     public void getTableRows() throws Exception {
291         final String htmlContent = DOCTYPE_HTML
292             + "<html><head><title>foo</title></head><body>\n"
293             + "<table id='table1'>\n"
294             + "<tr id='row1'><td>cell1</td></tr>\n"
295             + "<tr id='row2'><td>cell2</td></tr>\n"
296             + "<tr id='row3'><td>cell3</td></tr>\n"
297             + "<tr id='row4'><td>cell4</td></tr>\n"
298             + "<tr id='row5'><td>cell5</td></tr>\n"
299             + "<tr id='row6'><td>cell6</td></tr>\n"
300             + "</table>\n"
301             + "</body></html>";
302         final HtmlPage page = loadPage(htmlContent);
303 
304         final HtmlTable table = page.getHtmlElementById("table1");
305 
306         final List<HtmlTableRow> expectedRows = new ArrayList<>();
307         expectedRows.add(table.getRowById("row1"));
308         expectedRows.add(table.getRowById("row2"));
309         expectedRows.add(table.getRowById("row3"));
310         expectedRows.add(table.getRowById("row4"));
311         expectedRows.add(table.getRowById("row5"));
312         expectedRows.add(table.getRowById("row6"));
313 
314         assertEquals(expectedRows, table.getRows());
315     }
316 
317     /**
318      * @throws Exception if the test fails
319      */
320     @Test
321     public void getTableRows_WithHeadBodyFoot() throws Exception {
322         final String htmlContent = DOCTYPE_HTML
323             + "<html><head><title>foo</title></head><body>\n"
324             + "<table id='table1'>\n"
325             + "<thead>\n"
326             + "  <tr id='row1'><td>cell1</td></tr>\n"
327             + "  <tr id='row2'><td>cell2</td></tr>\n"
328             + "</thead>\n"
329             + "<tbody>\n"
330             + "  <tr id='row3'><td>cell3</td></tr>\n"
331             + "  <tr id='row4'><td>cell4</td></tr>\n"
332             + "</tbody>\n"
333             + "<tfoot>\n"
334             + "  <tr id='row5'><td>cell5</td></tr>\n"
335             + "  <tr id='row6'><td>cell6</td></tr>\n"
336             + "</tfoot>\n"
337             + "</table>\n"
338             + "</body></html>";
339         final HtmlPage page = loadPage(htmlContent);
340 
341         final HtmlTable table = page.getHtmlElementById("table1");
342 
343         final List<HtmlTableRow> expectedRows = new ArrayList<>();
344         expectedRows.add(table.getRowById("row1"));
345         expectedRows.add(table.getRowById("row2"));
346         expectedRows.add(table.getRowById("row3"));
347         expectedRows.add(table.getRowById("row4"));
348         expectedRows.add(table.getRowById("row5"));
349         expectedRows.add(table.getRowById("row6"));
350 
351         assertEquals(expectedRows, table.getRows());
352     }
353 
354     /**
355      * @throws Exception if the test fails
356      */
357     @Test
358     public void rowGroupings_AllDefined() throws Exception {
359         final String htmlContent = DOCTYPE_HTML
360             + "<html><head><title>foo</title></head><body>\n"
361             + "<table id='table1'>\n"
362             + "<thead>\n"
363             + "  <tr id='row1'><td>cell1</td></tr>\n"
364             + "  <tr id='row2'><td>cell2</td></tr>\n"
365             + "</thead>\n"
366             + "<tbody>\n"
367             + "  <tr id='row3'><td>cell3</td></tr>\n"
368             + "</tbody>\n"
369             + "<tbody>\n"
370             + "  <tr id='row4'><td>cell4</td></tr>\n"
371             + "</tbody>\n"
372             + "<tfoot>\n"
373             + "  <tr id='row5'><td>cell5</td></tr>\n"
374             + "  <tr id='row6'><td>cell6</td></tr>\n"
375             + "</tfoot>\n"
376             + "</table>\n"
377             + "</body></html>";
378         final HtmlPage page = loadPage(htmlContent);
379 
380         final HtmlTable table = page.getHtmlElementById("table1");
381 
382         assertNotNull(table.getHeader());
383         assertNotNull(table.getFooter());
384         assertEquals(2, table.getBodies().size());
385     }
386 
387     /**
388      * Check to ensure that the proper numbers of tags show up. Note that an extra tbody
389      * will be inserted to be in compliance with the common browsers.
390      * @throws Exception if the test fails
391      */
392     @Test
393     public void rowGroupings_NoneDefined()
394         throws Exception {
395 
396         final String htmlContent = DOCTYPE_HTML
397             + "<html><head><title>foo</title></head><body>\n"
398             + "<table id='table1'>\n"
399             + "  <tr id='row1'><td>cell1</td></tr>\n"
400             + "  <tr id='row2'><td>cell2</td></tr>\n"
401             + "  <tr id='row3'><td>cell3</td></tr>\n"
402             + "  <tr id='row4'><td>cell4</td></tr>\n"
403             + "  <tr id='row5'><td>cell5</td></tr>\n"
404             + "  <tr id='row6'><td>cell6</td></tr>\n"
405             + "</table>\n"
406             + "</body></html>";
407         final HtmlPage page = loadPage(htmlContent);
408 
409         final HtmlTable table = page.getHtmlElementById("table1");
410 
411         assertEquals(null, table.getHeader());
412         assertEquals(null, table.getFooter());
413         assertEquals(1, table.getBodies().size());
414     }
415 
416     /**
417      * @throws Exception if the test fails
418      */
419     @Test
420     public void getCaptionText() throws Exception {
421         final String htmlContent = DOCTYPE_HTML
422             + "<html><head><title>foo</title></head><body>\n"
423             + "<table id='table1' summary='Test table'>\n"
424             + "<caption>MyCaption</caption>\n"
425             + "<tr><td>cell1</td><td>cell2</td><td rowspan='2'>cell4</td></tr>\n"
426             + "<tr><td colspan='2'>cell3</td></tr>\n"
427             + "</table>\n"
428             + "</body></html>";
429         final HtmlPage page = loadPage(htmlContent);
430 
431         final HtmlTable table = page.getHtmlElementById("table1");
432 
433         assertEquals("MyCaption", table.getCaptionText());
434     }
435 
436     /**
437      * The common browsers will automatically insert tbody tags around the table rows if
438      * one wasn't specified. Ensure that we do this too. Also ensure that extra ones
439      * aren't inserted if a tbody was already there.
440      * @throws Exception if the test fails
441      */
442     @Test
443     public void insertionOfTbodyTags() throws Exception {
444         final String htmlContent = DOCTYPE_HTML
445             + "<html><head><title>foo</title></head><body>\n"
446             + "<table>\n"
447             + "<tr><td id='cell1'>cell1</td></tr>\n"
448             + "</table>\n"
449             + "<table><tbody>\n"
450             + "<tr><td id='cell2'>cell1</td></tr>\n"
451             + "</tbody></table>\n"
452             + "</body></html>";
453         final HtmlPage page = loadPage(htmlContent);
454 
455         // Check that a <tbody> was inserted properly
456         final HtmlTableDataCell cell1 = page.getHtmlElementById("cell1");
457         assertTrue(HtmlTableRow.class.isInstance(cell1.getParentNode()));
458         assertTrue(HtmlTableBody.class.isInstance(cell1.getParentNode().getParentNode()));
459         assertTrue(HtmlTable.class.isInstance(cell1.getParentNode().getParentNode().getParentNode()));
460 
461         // Check that the existing <tbody> wasn't messed up.
462         final HtmlTableDataCell cell2 = page.getHtmlElementById("cell2");
463         assertTrue(HtmlTableRow.class.isInstance(cell2.getParentNode()));
464         assertTrue(HtmlTableBody.class.isInstance(cell2.getParentNode().getParentNode()));
465         assertTrue(HtmlTable.class.isInstance(cell2.getParentNode().getParentNode().getParentNode()));
466     }
467 
468     /**
469      * @throws Exception if the test fails
470      */
471     @Test
472     public void asNormalizedText() throws Exception {
473         final String html = DOCTYPE_HTML
474             + "<html><head>\n"
475             + "</head><body>\n"
476             + "  <table id='myId'>\n"
477             + "  <caption>This is the caption</caption>\n"
478             + "  <tr>\n"
479             + "  <td>cell 1,1</td>\n"
480             + "  <td>cell 1,2</td>\n"
481             + "  </tr>\n"
482             + "  <tr>\n"
483             + "  <td>cell 2,1</td>\n"
484             + "  <td>cell 2,2</td>\n"
485             + "  </tr>\n"
486             + "  </table>\n"
487             + "</body></html>";
488 
489         final HtmlPage page = loadPage(html);
490         final HtmlElement table = page.getHtmlElementById("myId");
491         final String expectedText = "This is the caption\n"
492             + "cell 1,1\tcell 1,2\n"
493             + "cell 2,1\tcell 2,2";
494 
495         assertEquals(expectedText, table.asNormalizedText());
496     }
497 
498     /**
499      * @throws Exception if the test fails
500      */
501     @Test
502     public void asXml_emptyTable() throws Exception {
503         final String html = DOCTYPE_HTML
504             + "<html>\n"
505             + "<head/>\n"
506             + "<body>\n"
507             + "<div style=\"visibility: hidden\">\n"
508             + "<table>\n"
509             + "</table>\n"
510             + "</div>\n"
511             + "after the div\n"
512             + "</body>\n"
513             + "</html>";
514 
515         final HtmlPage page = loadPage(html);
516         assertTrue(page.asXml().contains("</table>"));
517     }
518 }