1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.html;
16
17 import java.lang.reflect.Method;
18 import java.util.ArrayList;
19 import java.util.HashSet;
20 import java.util.List;
21 import java.util.Locale;
22 import java.util.stream.Stream;
23
24 import org.htmlunit.BrowserVersion;
25 import org.htmlunit.MockWebConnection;
26 import org.htmlunit.WebClient;
27 import org.htmlunit.WebTestCase;
28 import org.htmlunit.html.parser.HTMLParser;
29 import org.junit.jupiter.api.Assertions;
30 import org.junit.jupiter.api.DynamicTest;
31 import org.junit.jupiter.api.TestFactory;
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 public class AttributesTest {
50
51 private static final List<String> EXCLUDED_METHODS = new ArrayList<>();
52 static {
53 EXCLUDED_METHODS.add("getHtmlElementsByAttribute");
54 EXCLUDED_METHODS.add("getOneHtmlElementByAttribute");
55 EXCLUDED_METHODS.add("getAttribute");
56 EXCLUDED_METHODS.add("getElementsByAttribute");
57 }
58
59
60
61
62
63
64
65 @TestFactory
66 Stream<DynamicTest> attributeTests() throws Exception {
67 final String[] classesToTest = {
68 "HtmlAbbreviated", "HtmlAcronym",
69 "HtmlAnchor", "HtmlAddress", "HtmlArea",
70 "HtmlArticle", "HtmlAside", "HtmlAudio",
71 "HtmlBackgroundSound", "HtmlBase", "HtmlBaseFont",
72 "HtmlBidirectionalIsolation",
73 "HtmlBidirectionalOverride", "HtmlBig",
74 "HtmlBlockQuote", "HtmlBody", "HtmlBold",
75 "HtmlBreak", "HtmlButton", "HtmlCanvas", "HtmlCaption",
76 "HtmlCenter", "HtmlCitation", "HtmlCode", "DomComment",
77 "HtmlData", "HtmlDataList",
78 "HtmlDefinition", "HtmlDefinitionDescription",
79 "HtmlDeletedText", "HtmlDetails", "HtmlDialog", "HtmlDirectory",
80 "HtmlDivision", "HtmlDefinitionList",
81 "HtmlDefinitionTerm", "HtmlEmbed",
82 "HtmlEmphasis",
83 "HtmlFieldSet", "HtmlFigureCaption", "HtmlFigure",
84 "HtmlFont", "HtmlForm", "HtmlFooter",
85 "HtmlFrame", "HtmlFrameSet",
86 "HtmlHead", "HtmlHeader",
87 "HtmlHeading1", "HtmlHeading2", "HtmlHeading3",
88 "HtmlHeading4", "HtmlHeading5", "HtmlHeading6",
89 "HtmlHorizontalRule", "HtmlHtml", "HtmlInlineFrame",
90 "HtmlInlineQuotation",
91 "HtmlImage", "HtmlImage", "HtmlInsertedText",
92 "HtmlItalic", "HtmlKeyboard", "HtmlLabel", "HtmlLayer",
93 "HtmlLegend", "HtmlListing", "HtmlListItem",
94 "HtmlLink",
95 "HtmlMap", "HtmlMain", "HtmlMark", "HtmlMarquee",
96 "HtmlMenu", "HtmlMeta", "HtmlMeter",
97 "HtmlNav",
98 "HtmlNoBreak", "HtmlNoEmbed", "HtmlNoFrames", "HtmlNoLayer",
99 "HtmlNoScript", "HtmlObject", "HtmlOrderedList",
100 "HtmlOptionGroup", "HtmlOption", "HtmlOutput",
101 "HtmlParagraph",
102 "HtmlParameter", "HtmlPicture", "HtmlPlainText", "HtmlPreformattedText",
103 "HtmlProgress",
104 "HtmlRb", "HtmlRp", "HtmlRt", "HtmlRtc", "HtmlRuby",
105 "HtmlS", "HtmlSample",
106 "HtmlScript", "HtmlSection", "HtmlSelect", "HtmlSlot", "HtmlSmall",
107 "HtmlSource", "HtmlSpan",
108 "HtmlStrike", "HtmlStrong", "HtmlStyle",
109 "HtmlSubscript", "HtmlSummary", "HtmlSuperscript",
110 "HtmlSvg",
111 "HtmlTable", "HtmlTableColumn", "HtmlTableColumnGroup",
112 "HtmlTableBody", "HtmlTableDataCell", "HtmlTableHeaderCell",
113 "HtmlTableRow", "HtmlTextArea", "HtmlTableFooter",
114 "HtmlTableHeader", "HtmlTeletype", "HtmlTemplate", "HtmlTrack",
115 "HtmlTime", "HtmlTitle",
116 "HtmlUnderlined", "HtmlUnorderedList",
117 "HtmlVariable", "HtmlVideo",
118 "HtmlWordBreak", "HtmlExample"
119 };
120
121 final HashSet<String> supportedTags = new HashSet<>(DefaultElementFactory.SUPPORTED_TAGS_);
122 final List<DynamicTest> tests = new ArrayList<>();
123
124 for (final String testClass : classesToTest) {
125 final Class<?> clazz = Class.forName("org.htmlunit.html." + testClass);
126 tests.addAll(createTestsForClass(clazz));
127
128 String tag;
129 if (DomComment.class == clazz) {
130 tag = "comment";
131 }
132 else {
133 tag = (String) clazz.getField("TAG_NAME").get(null);
134 }
135 supportedTags.remove(tag);
136 try {
137 tag = (String) clazz.getField("TAG_NAME2").get(null);
138 supportedTags.remove(tag);
139 }
140 catch (final NoSuchFieldException ignored) {
141
142 }
143 }
144
145 supportedTags.remove("input");
146
147 if (!supportedTags.isEmpty()) {
148 throw new RuntimeException("Missing tag class(es) " + supportedTags);
149 }
150 return tests.stream();
151 }
152
153
154
155
156
157
158
159 private List<DynamicTest> createTestsForClass(final Class<?> clazz) throws Exception {
160 final List<DynamicTest> tests = new ArrayList<>();
161 final Method[] methods = clazz.getMethods();
162
163 for (final Method method : methods) {
164 final String methodName = method.getName();
165 if (methodName.startsWith("get")
166 && methodName.endsWith("Attribute")
167 && !EXCLUDED_METHODS.contains(methodName)) {
168
169 final String attributeName =
170 normalizeAttributeName(
171 methodName.substring(3, methodName.length() - 9).toLowerCase(Locale.ROOT));
172
173 final String testName = createTestName(clazz, method);
174 tests.add(DynamicTest.dynamicTest(testName, () -> {
175 executeAttributeTest(clazz, method, attributeName);
176 }));
177 }
178 }
179 return tests;
180 }
181
182
183
184
185 private static String normalizeAttributeName(final String attributeName) {
186 switch (attributeName) {
187 case "xmllang": return "xml:lang";
188 case "columns": return "cols";
189 case "columnspan": return "colspan";
190 case "textdirection": return "dir";
191 case "httpequiv": return "http-equiv";
192 case "acceptcharset": return "accept-charset";
193 case "htmlfor": return "for";
194 default: return attributeName;
195 }
196 }
197
198
199
200
201
202
203
204
205 private static void executeAttributeTest(final Class<?> classUnderTest,
206 final Method method, final String attributeName) throws Exception {
207 try (WebClient webClient = new WebClient(BrowserVersion.BEST_SUPPORTED)) {
208 final MockWebConnection connection = new MockWebConnection();
209 connection.setDefaultResponse("<html><head><title>foo</title></head><body></body></html>");
210 webClient.setWebConnection(connection);
211 final HtmlPage page = webClient.getPage(WebTestCase.URL_FIRST);
212 final String value = "value";
213 final DomElement objectToTest = getNewInstanceForClassUnderTest(classUnderTest, page);
214 objectToTest.setAttribute(attributeName, value);
215 final Object[] noObjects = new Object[0];
216 final Object result = method.invoke(objectToTest, noObjects);
217 Assertions.assertSame(value, result);
218 }
219 }
220
221
222
223
224
225
226
227
228 private static String createTestName(final Class<?> clazz, final Method method) {
229 String className = clazz.getName();
230 final int index = className.lastIndexOf('.');
231 className = className.substring(index + 1);
232
233 return "testAttributes_" + className + '_' + method.getName();
234 }
235
236
237
238
239
240
241 private static DomElement getNewInstanceForClassUnderTest(
242 final Class<?> classUnderTest, final HtmlPage page) throws Exception {
243 final HTMLParser htmlParser = page.getWebClient().getPageCreator().getHtmlParser();
244 final DomElement newInstance;
245 if (classUnderTest == HtmlTableRow.class) {
246 newInstance = htmlParser.getFactory(HtmlTableRow.TAG_NAME)
247 .createElement(page, HtmlTableRow.TAG_NAME, null);
248 }
249 else if (classUnderTest == HtmlTableHeaderCell.class) {
250 newInstance = htmlParser.getFactory(HtmlTableHeaderCell.TAG_NAME)
251 .createElement(page, HtmlTableHeaderCell.TAG_NAME, null);
252 }
253 else if (classUnderTest == HtmlTableDataCell.class) {
254 newInstance = htmlParser.getFactory(HtmlTableDataCell.TAG_NAME)
255 .createElement(page, HtmlTableDataCell.TAG_NAME, null);
256 }
257 else {
258 final String tagName = (String) classUnderTest.getField("TAG_NAME").get(null);
259 newInstance = htmlParser.getFactory(tagName).createElement(page, tagName, null);
260 }
261
262 return newInstance;
263 }
264 }