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