1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.unitedinternet.portal.selenium.utils.logging;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.io.Writer;
22 import java.text.MessageFormat;
23 import java.text.SimpleDateFormat;
24 import java.util.Date;
25
26 import org.apache.commons.lang.ArrayUtils;
27 import org.apache.commons.lang.StringUtils;
28
29
30
31
32
33
34
35
36
37
38
39
40 public class HtmlResultFormatter implements LoggingResultsFormatter {
41 String resultFileEncoding = "ISO-8859-1";
42
43 static final int HTML_MAX_COLUMNS = 7;
44
45 static final int SCREENSHOT_PREVIEW_HEIGHT = 200;
46
47 static final int SCREENSHOT_PREVIEW_WIDHT = 200;
48
49 static final String URL_PATH_SEPARATOR = "/";
50
51 static final String CSS_CLASS_FAILED = "status_failed";
52
53 static final String CSS_CLASS_PASSED = "status_passed";
54
55 static final String CSS_CLASS_UNKNOWN = "status_maybefailed";
56
57 static final String CSS_CLASS_DONE = "status_done";
58
59 static final String CSS_CLASS_TITLE = "title";
60
61 static final String TOOL_TIPP_MESSAGE_TIME_DELTA = "time delta reporting is alpha and subject to change";
62
63 static final SimpleDateFormat LOGGING_DATETIME_FORMAT = new SimpleDateFormat("HH:mm:ss dd-MM-yyyy");
64
65 static SimpleDateFormat FILENAME_DATETIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH-mm");
66
67
68 String localFsPathSeparator = File.separator;
69
70 String screenShotBaseUri = "";
71
72 String automaticScreenshotPath = ".";
73
74 Writer resultsWriter = null;
75
76
77 static final String HTML_HEADER = "<html>\n"
78 + "<head>"
79 + "<meta content=\"text/html; charset={0}\" http-equiv=\"content-type\">"
80 + "<meta content=\"cache-control\" http-equiv=\"no-cache\">"
81 + "<meta content=\"pragma\" http-equiv=\"no-cache\">"
82 + "<style type=\"text/css\">\n"
83 + "body, table '{'\n"
84 + " font-family: Verdana, Arial, sans-serif;\n"
85 + " font-size: 12;\n"
86 + "'}'\n"
87 + "\n"
88 + "table '{'\n"
89 + " border-collapse: collapse;\n"
90 + " border: 1px solid #ccc;\n"
91 + "'}'\n"
92 + "\n"
93 + "th, td '{'\n"
94 + " padding-left: 0.3em;\n"
95 + " padding-right: 0.3em;\n"
96 + "'}'\n"
97 + "\n"
98 + "a '{'\n"
99 + " text-decoration: none;\n"
100 + "'}'\n"
101 + "\n"
102 + "."
103 + CSS_CLASS_TITLE
104 + " '{'\n"
105 + " font-style: italic;\n"
106 + "'}'\n"
107 + "\n"
108 + ".selected '{'\n"
109 + " background-color: #ffffcc;\n"
110 + "'}'\n"
111 + "\n"
112 + "."
113 + CSS_CLASS_DONE
114 + " '{'\n"
115 + " background-color: #eeffee;\n"
116 + "'}'\n"
117 + "\n"
118 + "."
119 + CSS_CLASS_PASSED
120 + " '{'\n"
121 + " background-color: #ccffcc;\n"
122 + "'}'\n"
123 + "\n"
124 + "."
125 + CSS_CLASS_FAILED
126 + " '{'\n"
127 + " background-color: #ffcccc;\n"
128 + "'}'\n"
129 + "\n"
130 + "."
131 + CSS_CLASS_UNKNOWN
132 + " '{'\n"
133 + " background-color: #ffffcc;\n"
134 + "'}'\n"
135 + "\n"
136 + ".breakpoint '{'\n"
137 + " background-color: #cccccc;\n"
138 + " border: 1px solid black;\n"
139 + "'}'\n"
140 + "</style>\n"
141 + "<title>Test results</title></head>\n"
142 + "<body>\n"
143 + " <span style=\"font-size:9px;font-family:arial,verdana,sans-serif;\">"
144 + "HTML Logging of Junit driven Selenium-RC Tests. Contributed by Robert Zimmermann (unitedinternet.com)"
145 + "</span>"
146 + "<h1>Test results </h1>";
147
148 static final String HTML_TABLE_HEADER = "<tr>"
149 + "<td><b>Selenium-Command</b></td>"
150 + "<td><b>Parameter-1</b></td>"
151 + "<td><b>Parameter-2</b></td>"
152 + "<td><b>Res.RC</b></td>"
153 + "<td><b>Res.Selenium</b></td>"
154 + "<td><b>Time [ms]</b></td>"
155 + "<td><b>Calling-Class with Linenumber</b></td>"
156 + "</tr>\n";
157
158 static final String HTML_METRICS = "<table>\n"
159 + "<tr><td>user-agent:</td><td>{0}</td></tr>\n"
160 + "<tr><td>selenium-rc:</td><td> v{1} [{2}]</td></tr>\n"
161 + "<tr><td>selenium-core:</td><td> v{3} [{4}]</td></tr>\n"
162 + "<tr><td>LoggingSelenium:</td><td> revision [{5}]</td></tr>\n"
163 + "<tr><td>test-started:</td><td>{6}</td></tr>\n"
164 + "<tr><td>test-finished:</td><td>{7}</td></tr>\n"
165 + "<tr><td>test-duration [ms]:</td><td>{8}</td></tr>\n"
166 + "<tr><td>commands processed:</td><td>{9}</td></tr>\n"
167 + "<tr><td>verifications processed:</td><td>{10}</td></tr>\n"
168 + "{11}\n"
169 + "<tr><td>commands not logged:</td><td>{12}</td></tr>\n"
170 + "</table>\n";
171
172 static final String HTML_COMMENT = "<tr class=\"{0}\"><td colspan=\"{1}\">{2}</td></tr>\n";
173
174 static final String HTML_FOOTER = "</tbody></table></body></html>";
175
176 static final String HTML_SPECIAL = "<span style=\"font-size:9px;font-family:arial,verdana,sans-serif;\">" + "{0}</span>";
177
178 static final String HTML_SCREENSHOT_ROW = "<tr class=\"{0}\">"
179 + "<td colspan=\"{1}\" valign=\"center\" align=\"center\" halign=\"center\">{2}</td>"
180 + "<td>{3}</td>"
181 + "<td>{4}</td>"
182 + "</tr>\n";
183
184 static final String HTML_SCREENSHOT_IMG = "<a href=\"{0}\">"
185 + "<img src=\"{1}\" width=\"{2}\" height=\"{3}\""
186 + " alt=\"Selenium Screenshot\""
187 + " title=\"Selenium Screenshot\"/>"
188 + "<br/>{4}</a>";
189
190 static final String HTML_EMPTY_COLUMN = "<td> </td>";
191
192
193
194
195
196
197
198
199
200 public HtmlResultFormatter(Writer myResultsWriter) {
201 this.resultsWriter = myResultsWriter;
202 }
203
204
205
206
207
208
209
210
211
212
213
214
215
216 public HtmlResultFormatter(Writer myResultsWriter, String myResultFileEncoding) {
217 this.resultsWriter = myResultsWriter;
218 this.resultFileEncoding = myResultFileEncoding;
219 }
220
221
222
223
224 public void commentLogEvent(LoggingBean loggingBean) {
225 String[] loggingBeanArgs = LoggingUtils.getCorrectedArgsArray(loggingBean, 2, "");
226 String commentToBeLogged = loggingBeanArgs[0];
227 String additionalInformation = loggingBeanArgs[1];
228 logToWriter(MessageFormat.format(HTML_COMMENT, CSS_CLASS_TITLE, HTML_MAX_COLUMNS, commentToBeLogged
229 + extraInformationLogEvent(additionalInformation)));
230 }
231
232 String formatMetrics(TestMetricsBean metrics) {
233 String failedCommandsRow = "";
234 if (metrics.getFailedCommands() > 0) {
235 failedCommandsRow = "<tr class=\""
236 + CSS_CLASS_FAILED
237 + "\"><td>failed commands:</td><td>"
238 + metrics.getFailedCommands()
239 + "</td></tr>\n";
240 if (StringUtils.isNotBlank(metrics.getLastFailedCommandMessage())) {
241 failedCommandsRow = failedCommandsRow
242 + "<tr class=\""
243 + CSS_CLASS_FAILED
244 + "\"><td>last failed message:</td><td>"
245 + metrics.getLastFailedCommandMessage()
246 + "</td></tr>\n";
247 } else {
248 System.err.println("WARNING: NO LastFailedCommandMessage");
249 }
250 }
251 return MessageFormat.format(HTML_METRICS, metrics.getUserAgent(), metrics.getSeleniumRcVersion(), metrics
252 .getSeleniumRcRevision(), metrics.getSeleniumCoreVersion(), metrics.getSeleniumCoreRevision(), metrics
253 .getLoggingSeleniumRevision(), LOGGING_DATETIME_FORMAT.format(metrics.getStartTimeStamp()),
254 LOGGING_DATETIME_FORMAT.format(metrics.getEndTimeStamp()), metrics.getTestDuration(), metrics
255 .getCommandsProcessed(), metrics.getVerificationsProcessed(), failedCommandsRow, ArrayUtils
256 .toString(metrics.getCommandsExcludedFromLogging()));
257 }
258
259
260
261
262 public void headerLogEvent(TestMetricsBean metrics) {
263 logToWriter(formatHeader(metrics));
264 }
265
266 String formatHeader(TestMetricsBean metrics) {
267 String header = MessageFormat.format(HTML_HEADER, resultFileEncoding)
268 + "\n"
269 + formatMetrics(metrics)
270 + "<table border=\"1\"><tbody>"
271 + HTML_TABLE_HEADER;
272 return header;
273 }
274
275
276
277
278 public void footerLogEvent() {
279 logToWriter(HTML_FOOTER);
280 }
281
282 String extraInformationLogEvent(String extraInformation) {
283 String result = "";
284 if (null != extraInformation) {
285 result = MessageFormat.format(HTML_SPECIAL, extraInformation);
286 }
287 return result;
288 }
289
290
291
292
293 public void commandLogEvent(LoggingBean loggingBean) {
294 if (!loggingBean.isExcludeFromLogging()) {
295 String resultClass = loggingBean.isCommandSuccessful() ? CSS_CLASS_DONE : CSS_CLASS_FAILED;
296 if ("captureScreenshot".equals(loggingBean.getCommandName())) {
297 logToWriter(formatScreenshot(loggingBean, resultClass));
298 } else {
299 logToWriter(formatCommandAsHtml(loggingBean, resultClass, ""));
300 }
301 }
302 }
303
304
305
306
307 public void booleanCommandLogEvent(LoggingBean loggingBean) {
308 String toolTippMessage = "";
309 String resultClass = "";
310 if (loggingBean.isCommandSuccessful()) {
311 resultClass = CSS_CLASS_PASSED;
312 } else {
313 resultClass = CSS_CLASS_UNKNOWN;
314 toolTippMessage = "How this "false" result from Selenium"
315 + " is treated by the test cannot be determined here.";
316 }
317 logToWriter(formatCommandAsHtml(loggingBean, resultClass, toolTippMessage));
318 }
319
320
321
322
323 public String getScreenShotBaseUri() {
324 return screenShotBaseUri;
325 }
326
327
328
329
330 public void setScreenShotBaseUri(String screenShotBaseUri) {
331 this.screenShotBaseUri = screenShotBaseUri == null ? "" : screenShotBaseUri;
332 }
333
334
335
336
337 public String generateFilenameForAutomaticScreenshot(String baseName) {
338 final String constWaitTimeoutScreenshotFileName = "automatic" + baseName + "Screenshot" + timeStampForFileName() + ".png";
339 return this.automaticScreenshotPath + localFsPathSeparator + constWaitTimeoutScreenshotFileName;
340
341 }
342
343
344
345
346 public String getAutomaticScreenshotPath() {
347 return this.automaticScreenshotPath;
348 }
349
350
351
352
353
354
355
356
357 public void setAutomaticScreenshotPath(String automaticScreenshotPath) {
358 this.automaticScreenshotPath = new File(automaticScreenshotPath).getAbsolutePath();
359 }
360
361 String formatScreenshot(LoggingBean loggingBean, String resultClass) {
362
363 return MessageFormat.format(HTML_SCREENSHOT_ROW, resultClass, HTML_MAX_COLUMNS - 2,
364 formatScreenshotFileImgTag(loggingBean.getArgs()[0]), +loggingBean.getDeltaMillis(), loggingBean
365 .getCallingClass());
366 }
367
368
369
370
371
372
373
374
375 String formatScreenshotFileImgTag(String absFsPathToScreenshot) {
376 String screenshotRelativeUrl;
377 String screenshotPathNormalized = absFsPathToScreenshot.replace(localFsPathSeparator, URL_PATH_SEPARATOR);
378 String screenShotName = screenshotPathNormalized.substring(screenshotPathNormalized.lastIndexOf(URL_PATH_SEPARATOR)
379 + URL_PATH_SEPARATOR.length());
380
381 if ("".equals(this.screenShotBaseUri)) {
382 screenshotRelativeUrl = screenShotName;
383 } else {
384 screenshotRelativeUrl = this.screenShotBaseUri.endsWith("/") ? this.screenShotBaseUri + screenShotName
385 : this.screenShotBaseUri + URL_PATH_SEPARATOR + screenShotName;
386 }
387 return MessageFormat.format(HTML_SCREENSHOT_IMG, screenshotRelativeUrl, screenshotRelativeUrl, SCREENSHOT_PREVIEW_WIDHT,
388 SCREENSHOT_PREVIEW_HEIGHT, screenShotName);
389 }
390
391 String formatCommandAsHtml(LoggingBean loggingBean, String resultClass, String toolTippMessage) {
392 StringBuilder htmlWrappedCommand = new StringBuilder();
393 htmlWrappedCommand.append("<tr class=\""
394 + resultClass
395 + "\" title=\""
396 + toolTippMessage
397 + "\" alt=\""
398 + toolTippMessage
399 + "\">"
400 + "<td>"
401 + quoteHtml(loggingBean.getCommandName())
402 + "</td>");
403 int writtenColumns = 0;
404 if (loggingBean.getArgs() != null) {
405 for (int i = 0; i < loggingBean.getArgs().length; i++) {
406 writtenColumns++;
407 htmlWrappedCommand.append("<td>" + quoteHtml(loggingBean.getArgs()[i]) + "</td>");
408 }
409 }
410
411 htmlWrappedCommand.append(generateEmptyColumns(HTML_MAX_COLUMNS - writtenColumns - (HTML_MAX_COLUMNS - 2)));
412
413 htmlWrappedCommand.append("<td>"
414 + quoteHtml(loggingBean.getSrcResult())
415 + "</td><td>"
416 + quoteHtml(loggingBean.getSelResult())
417 + "</td><td title=\""
418 + TOOL_TIPP_MESSAGE_TIME_DELTA
419 + "\" alt=\""
420 + TOOL_TIPP_MESSAGE_TIME_DELTA
421 + "\">"
422 + loggingBean.getDeltaMillis()
423 + "</td><td>"
424 + loggingBean.getCallingClass()
425 + "</td></tr>\n");
426 return htmlWrappedCommand.toString();
427 }
428
429 void logToWriter(final String formattedLogEvent) {
430 try {
431 this.resultsWriter.write(formattedLogEvent);
432 } catch (IOException e) {
433 throw new RuntimeException(e);
434 }
435 }
436
437
438
439
440
441
442
443 public static final String generateEmptyColumns(final int numColsToGenerate) {
444 StringBuilder result = new StringBuilder();
445 for (int i = 0; i < numColsToGenerate; i++) {
446 result.append(HTML_EMPTY_COLUMN);
447 }
448 return result.toString();
449 }
450
451
452
453
454
455
456
457
458
459 public static final String timeStampForFileName() {
460 Date currentDateTime = new Date(System.currentTimeMillis());
461 return FILENAME_DATETIME_FORMAT.format(currentDateTime);
462 }
463
464
465
466
467 public void methodLogEvent(LoggingBean loggingBean) {
468 }
469
470 public static final String quoteHtml(String unquoted) {
471 String quoted = unquoted == null ? "" : unquoted;
472 quoted = quoted.replace("&", "&");
473 quoted = quoted.replace("<", "<");
474 quoted = quoted.replace(">", ">");
475 return quoted;
476 }
477
478 public static SimpleDateFormat getFILENAME_DATETIME_FORMAT() {
479 return FILENAME_DATETIME_FORMAT;
480 }
481
482 public static void setFILENAME_DATETIME_FORMAT(SimpleDateFormat newFormat) {
483 FILENAME_DATETIME_FORMAT = newFormat;
484 }
485 }