1 |
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> |
2 |
<html> |
3 |
<head> |
4 |
<title>JSCoverage user manual</title> |
5 |
<link type="text/css" href="doc.css" rel="stylesheet"> |
6 |
</head> |
7 |
<body> |
8 |
|
9 |
<h1>JSCoverage user manual</h1> |
10 |
|
11 |
<p> |
12 |
JSCoverage is a tool used to measure code coverage in JavaScript programs. |
13 |
</p> |
14 |
|
15 |
<p> |
16 |
JSCoverage works by adding instrumentation to JavaScript code before it is |
17 |
executed in a web browser. JSCoverage provides several alternative ways of doing |
18 |
this: |
19 |
</p> |
20 |
|
21 |
<ul> |
22 |
<li>The simplest method is to use the <code>jscoverage</code> program to generate |
23 |
instrumented JavaScript files. |
24 |
</li> |
25 |
<li>Alternatively, you can use the <code>jscoverage-server</code> program, a simple web server that instruments |
26 |
JavaScript code as it is served. |
27 |
</li> |
28 |
<li>Finally, <code>jscoverage-server</code> can be run with the <code>--proxy</code> option to |
29 |
act as a proxy server which instruments any JavaScript code proxied through it. |
30 |
</li> |
31 |
</ul> |
32 |
|
33 |
<p> |
34 |
The <code>jscoverage-server</code> program (with or without the <code>--proxy</code> |
35 |
option) has the advantage of being able to store coverage reports to the filesystem. |
36 |
</p> |
37 |
|
38 |
<h2>Installing JSCoverage</h2> |
39 |
|
40 |
<p> |
41 |
You can compile JSCoverage on GNU/Linux or Microsoft Windows, using GCC. On |
42 |
Windows you will require <a href="http://cygwin.com/">Cygwin</a> or <a |
43 |
href="http://mingw.org/">MinGW/MSYS</a>. |
44 |
</p> |
45 |
|
46 |
<p> |
47 |
You can extract and compile the code with the following commands: |
48 |
</p> |
49 |
|
50 |
<pre> |
51 |
tar jxvf jscoverage-0.4.tar.bz2 |
52 |
cd jscoverage-0.4/ |
53 |
./configure |
54 |
make |
55 |
</pre> |
56 |
|
57 |
<p> |
58 |
This will create the <code>jscoverage</code> and <code>jscoverage-server</code> |
59 |
executables (<code>jscoverage.exe</code> and <code>jscoverage-server.exe</code> |
60 |
on Windows). You can install the executables in <code>/usr/local</code> with the |
61 |
command: |
62 |
</p> |
63 |
|
64 |
<pre> |
65 |
make install |
66 |
</pre> |
67 |
|
68 |
<p> |
69 |
Alternatively, you may simply copy the <code>jscoverage</code> executable and/or |
70 |
the <code>jscoverage-server</code> executable to a suitable location in your |
71 |
<code>PATH</code>. |
72 |
</p> |
73 |
|
74 |
<h2>Using the <code>jscoverage</code> program</h2> |
75 |
|
76 |
<p> |
77 |
Using the <code>jscoverage</code> program requires the following steps: |
78 |
</p> |
79 |
|
80 |
<h3>1. Instrumenting code</h3> |
81 |
|
82 |
<p> |
83 |
The first step is to add instrumentation to your JavaScript code. You do this by |
84 |
executing <code>jscoverage</code> with two arguments: |
85 |
</p> |
86 |
|
87 |
<pre> |
88 |
jscoverage <var>SOURCE-DIRECTORY</var> <var>DESTINATION-DIRECTORY</var> |
89 |
</pre> |
90 |
|
91 |
<p> |
92 |
<var>SOURCE-DIRECTORY</var> is the directory containing the JavaScript code to be instrumented, |
93 |
and <var>DESTINATION-DIRECTORY</var> is the name of the |
94 |
directory to which <code>jscoverage</code> should output the instrumented code. |
95 |
The <code>jscoverage</code> program will create <var>DESTINATION-DIRECTORY</var> if necessary and (recursively) copy |
96 |
<var>SOURCE-DIRECTORY</var> to <var>DESTINATION-DIRECTORY</var>, instrumenting |
97 |
any files ending with a <code>.js</code> extension. |
98 |
</p> |
99 |
|
100 |
<p> |
101 |
For example, if you have a file |
102 |
<code><var>SOURCE-DIRECTORY</var>/dir/index.html</code> referencing the script |
103 |
<code><var>SOURCE-DIRECTORY</var>/dir/script.js</code>, then |
104 |
<code>jscoverage</code> will create a copy of the HTML file at |
105 |
<code><var>DESTINATION-DIRECTORY</var>/dir/index.html</code> and an instrumented |
106 |
version of the script at |
107 |
<code><var>DESTINATION-DIRECTORY</var>/dir/script.js</code>. |
108 |
</p> |
109 |
|
110 |
<table> |
111 |
<tr> |
112 |
<td><pre> |
113 |
<var>SOURCE-DIRECTORY</var>/ |
114 |
dir/ |
115 |
index.html |
116 |
script.js |
117 |
|
118 |
</pre></td> |
119 |
<td class="arrow">→</td> |
120 |
<td><pre> |
121 |
<var>DESTINATION-DIRECTORY</var>/ |
122 |
dir/ |
123 |
index.html |
124 |
script.js [instrumented] |
125 |
jscoverage.html |
126 |
</pre></td> |
127 |
</tr> |
128 |
</table> |
129 |
|
130 |
<p> |
131 |
In addition, <code>jscoverage</code> creates a file called <code>jscoverage.html</code> |
132 |
which is used to execute the instrumented code. |
133 |
</p> |
134 |
|
135 |
<h3>2. Executing the instrumented code in a web browser</h3> |
136 |
|
137 |
<p> |
138 |
Open <code>jscoverage.html</code> in your web browser. |
139 |
The page contains a tabbed user interface: |
140 |
</p> |
141 |
|
142 |
<ul> |
143 |
<li>The "Browser" tab is used to display pages with instrumented scripts. |
144 |
<li>The "Summary" tab is used to display code coverage data. |
145 |
<li>The "Source" tab is used to display JavaScript code, showing the number of times |
146 |
each line of code was executed. |
147 |
<li>The "About" tab displays information about the current version of JSCoverage. |
148 |
</ul> |
149 |
|
150 |
<img src="screenshot.png" alt="Screenshot"> |
151 |
|
152 |
<p> |
153 |
The "Browser" tab contains an <code><iframe></code>, which is initially empty. |
154 |
You can load a page into this frame by |
155 |
entering its URL into the "URL" input field. For example, to load |
156 |
the file <code><var>DESTINATION-DIRECTORY</var>/dir/index.html</code>, you can |
157 |
enter the relative URL <code>dir/index.html</code> into the input field. |
158 |
You can load any page located in <code><var>DESTINATION-DIRECTORY</var>/</code> |
159 |
or a subdirectory underneath <code><var>DESTINATION-DIRECTORY</var>/</code>; loading a page |
160 |
from outside <code><var>DESTINATION-DIRECTORY</var>/</code>, or from a foreign web |
161 |
server, will give unexpected results. |
162 |
</p> |
163 |
|
164 |
<h3>3. Generating a coverage report</h3> |
165 |
|
166 |
<p> |
167 |
Once the JavaScript code in the page in the "Browser" tab has been executed, click on |
168 |
the "Summary" tab. This will display the current code coverage statistics. |
169 |
</p> |
170 |
|
171 |
<p> |
172 |
As long as you do not reload the |
173 |
<code>jscoverage.html</code> page, the coverage report statistics are |
174 |
cumulative. If you execute more JavaScript in the frame in the "Browser" tab (e.g., by clicking on a link to |
175 |
another scripted page, or by reloading the frame containing a scripted |
176 |
page) and switch to the "Summary" tab again, |
177 |
the coverage report will combine the statistics from the previous report with any newly generated statistics. |
178 |
Reloading <code>jscoverage.html</code> resets all code coverage statistics to zero. |
179 |
</p> |
180 |
|
181 |
<h2>Example</h2> |
182 |
|
183 |
<p> |
184 |
The JSCoverage distribution comes with a trivial example program in the <code>doc/example</code> directory. |
185 |
You can view the file <code>doc/example/index.html</code> in your web browser to run the (uninstrumented) program. |
186 |
To instrument this program, follow these steps: |
187 |
</p> |
188 |
|
189 |
<h3>1. Instrumenting code</h3> |
190 |
|
191 |
<p> |
192 |
From the main distribution directory, execute the command: |
193 |
</p> |
194 |
|
195 |
<pre> |
196 |
jscoverage doc/example doc/instrumented |
197 |
</pre> |
198 |
|
199 |
<p> |
200 |
This will create the directory <code>doc/instrumented</code> and |
201 |
place an instrumented copy of the code from <code>doc/example</code> in <code>doc/instrumented</code>. |
202 |
</p> |
203 |
|
204 |
<h3>2. Executing the instrumented code in a web browser</h3> |
205 |
|
206 |
<p> |
207 |
You can load the file <code>doc/instrumented/jscoverage.html</code> in your web browser and type |
208 |
the URL for the instrumented code in the "URL" input field. Since a relative URL is accepted, you |
209 |
can simply type <code>index.html</code> to load the page. |
210 |
</p> |
211 |
|
212 |
<p> |
213 |
Alternatively, you can append the URL to the query string of the |
214 |
<code>jscoverage.html</code> URL; for example, if you are in the main JSCoverage |
215 |
directory and the Firefox executable is in your <code>PATH</code>, you can load |
216 |
the <code>jscoverage.html</code> frameset and the <code>index.html</code> page |
217 |
all in one command line: |
218 |
</p> |
219 |
|
220 |
<pre> |
221 |
firefox "doc/instrumented/jscoverage.html?index.html" |
222 |
</pre> |
223 |
|
224 |
<img src="screenshot2.png" alt="Screenshot"> |
225 |
|
226 |
<p> |
227 |
For this particular page, the JavaScript does not execute automatically: |
228 |
you have to select one of the radio buttons to execute the code. |
229 |
</p> |
230 |
|
231 |
<img src="screenshot3.png" alt="Screenshot"> |
232 |
|
233 |
<h3>3. Generating a coverage report</h3> |
234 |
|
235 |
<p> |
236 |
Once you have executed the JavaScript code, you are instructed to click on the |
237 |
"Summary" tab. |
238 |
</p> |
239 |
|
240 |
<img src="screenshot4.png" alt="Screenshot"> |
241 |
|
242 |
<p> |
243 |
You can click the checkbox to show a list of statements missed during execution. |
244 |
</p> |
245 |
|
246 |
<img src="screenshot5.png" alt="Screenshot"> |
247 |
|
248 |
<p> |
249 |
You can click one of the links to get a detailed view of a JavaScript source file. |
250 |
</p> |
251 |
|
252 |
<img src="screenshot6.png" alt="Screenshot"> |
253 |
|
254 |
<h2>Inverted mode</h2> |
255 |
|
256 |
<p> |
257 |
In some situations it may be difficult to execute your code within the |
258 |
JSCoverage "Browser" tab. For example, the code may assume that it is running in |
259 |
the top-level browser window, generating errors if it is executed from within a |
260 |
frame. JSCoverage has an alternative mode of operation, called <dfn>inverted |
261 |
mode</dfn>, which may be useful in this case. |
262 |
</p> |
263 |
|
264 |
<p> |
265 |
Normally you load <code>jscoverage.html</code> in your web browser, and in its |
266 |
"Browser" tab you launch your test code. In inverted mode, you do the |
267 |
opposite: you load your test page directly in your web browser, and from there |
268 |
you launch JSCoverage. To do this you need to add some code to your test page: |
269 |
</p> |
270 |
|
271 |
<pre> |
272 |
window.open("path/to/jscoverage.html"); |
273 |
</pre> |
274 |
|
275 |
<p> |
276 |
The <code>"path/to/jscoverage.html"</code> should be a URL pointing to the |
277 |
location of the <code>jscoverage.html</code> file (remember, this will be in the |
278 |
top level of the <var>DESTINATION-DIRECTORY</var> you specified when running |
279 |
the <code>jscoverage</code> executable). |
280 |
</p> |
281 |
|
282 |
<p> |
283 |
You can place this code wherever you like in your page: for example, you could |
284 |
attach it to a button: |
285 |
</p> |
286 |
|
287 |
<pre> |
288 |
<button onclick='window.open("path/to/jscoverage.html");'>Coverage report</button> |
289 |
</pre> |
290 |
|
291 |
<p> |
292 |
Note that you <em>must</em> use a <code>window.open</code> call; simply making a |
293 |
link to <code>jscoverage.html</code> is not sufficient. |
294 |
</p> |
295 |
|
296 |
<p> |
297 |
An example is located in the <code>doc/example-inverted</code> directory. |
298 |
You can instrument the code and launch the <code>index.html</code> page: |
299 |
</p> |
300 |
|
301 |
<pre> |
302 |
jscoverage doc/example-inverted doc/instrumented-inverted |
303 |
firefox "doc/instrumented-inverted/index.html" |
304 |
</pre> |
305 |
|
306 |
<p> |
307 |
From this page, you select one of the radio buttons and then click the "Coverage |
308 |
report" button to launch the JSCoverage report. |
309 |
</p> |
310 |
|
311 |
<h2><code>jscoverage</code> command line options</h2> |
312 |
|
313 |
<p> |
314 |
The <code>jscoverage</code> program accepts the following options: |
315 |
</p> |
316 |
|
317 |
<dl> |
318 |
<dt><code>-h</code>, <code>--help</code> |
319 |
<dd>Display a brief help message. |
320 |
<dt><code>-V</code>, <code>--version</code> |
321 |
<dd>Display the version of the program. |
322 |
<dt><code>-v</code>, <code>--verbose</code> |
323 |
<dd>Explain what is being done. |
324 |
<dt><code>--exclude=<var>PATH</var></code> |
325 |
<dd>The command |
326 |
<pre> |
327 |
jscoverage --exclude=<var>PATH</var> <var>SOURCE-DIRECTORY</var> <var>DESTINATION-DIRECTORY</var> |
328 |
</pre> |
329 |
copies <var>SOURCE-DIRECTORY</var> to <var>DESTINATION-DIRECTORY</var> |
330 |
recursively, but does not copy <var>SOURCE-DIRECTORY</var>/<var>PATH</var>. |
331 |
<var>PATH</var> must be a complete path relative to <var>SOURCE-DIRECTORY</var>. |
332 |
<var>PATH</var> can be a file or a directory (in which case the directory and |
333 |
its entire contents are skipped). This option may be given multiple times. |
334 |
<dt><code>--no-instrument=<var>PATH</var></code> |
335 |
<dd>The command |
336 |
<pre> |
337 |
jscoverage --no-instrument=<var>PATH</var> <var>SOURCE-DIRECTORY</var> <var>DESTINATION-DIRECTORY</var> |
338 |
</pre> |
339 |
copies <var>SOURCE-DIRECTORY</var> to <var>DESTINATION-DIRECTORY</var> |
340 |
recursively, but does not instrument any JavaScript code in |
341 |
<var>SOURCE-DIRECTORY</var>/<var>PATH</var>. <var>PATH</var> must be a complete |
342 |
path relative to <var>SOURCE-DIRECTORY</var>. <var>PATH</var> can be a |
343 |
(JavaScript) file or a directory (in which case any JavaScript files located |
344 |
anywhere underneath the directory are not instrumented). This option may be |
345 |
given multiple times. |
346 |
</dl> |
347 |
|
348 |
<h2>Query string options</h2> |
349 |
|
350 |
<p> |
351 |
When accessing <code>jscoverage.html</code> in a web browser, you may provide a |
352 |
query string consisting of options separated by ampersand (<code>&</code>) |
353 |
or semicolon (<code>;</code>). Any option not containing an equals sign |
354 |
(<code>=</code>) is considered to be a URL which will be loaded in the "Browser" |
355 |
tab. |
356 |
</p> |
357 |
|
358 |
<dl> |
359 |
<dt><code>u=<var>URL</var></code>, <code>url=<var>URL</var></code> |
360 |
<dd>Load <var>URL</var> in the "Browser" tab. (This is the same as specifying |
361 |
an option without an equals sign.) |
362 |
<dt><code>m=<var>BOOLEAN</var></code>, <code>missing=<var>BOOLEAN</var></code> |
363 |
<dd>Determines whether to initially display the "Missing" column in the "Summary" |
364 |
tab. <var>BOOLEAN</var> can be |
365 |
<code>true</code>, <code>t</code>, <code>yes</code>, <code>y</code>, <code>on</code>, <code>1</code> |
366 |
(to display the "Missing" column), or |
367 |
<code>false</code>, <code>f</code>, <code>no</code>, <code>n</code>, <code>off</code>, <code>0</code> |
368 |
(to hide the "Missing" column). By default, the "Missing" column is not displayed. |
369 |
</dl> |
370 |
|
371 |
<h2>Using the <code>jscoverage-server</code> program</h2> |
372 |
|
373 |
<p> |
374 |
The <code>jscoverage-server</code> program is a simple web server which will |
375 |
serve files from the current directory. You can use |
376 |
<code>jscoverage-server</code> to serve <code>doc/example</code>: |
377 |
</p> |
378 |
|
379 |
<pre> |
380 |
cd doc/example |
381 |
jscoverage-server --verbose |
382 |
</pre> |
383 |
|
384 |
<p> |
385 |
By default, the server runs on port 8080. URLs are mapped to files in the |
386 |
<code>doc/example</code> directory: e.g., the URL |
387 |
<code>http://127.0.0.1:8080/index.html</code> can be used to request |
388 |
<code>doc/example/index.html</code>. In addition, the special URL |
389 |
<code>http://127.0.0.1:8080/jscoverage.html</code> provides the JSCoverage web |
390 |
interface. Note that it is not necessary that a file named |
391 |
<code>jscoverage.html</code> exist; the JSCoverage web |
392 |
interface is built into the server, and it will automatically be served when the special |
393 |
<code>/jscoverage.html</code> URL is requested. |
394 |
</p> |
395 |
|
396 |
<p> |
397 |
You can use the <code>/jscoverage.html</code> URL in the same way as the |
398 |
<code>jscoverage.html</code> file generated by the <code>jscoverage</code> |
399 |
program. For example, you can visit the URL |
400 |
<code>http://127.0.0.1:8080/jscoverage.html?index.html</code> to execute the |
401 |
JavaScript associated with the <code>index.html</code> page. The |
402 |
<code>jscoverage-server</code> program automatically instruments all served |
403 |
JavaScript code, so that code coverage data will be gathered as the code is |
404 |
executed in your browser. |
405 |
</p> |
406 |
|
407 |
<p> |
408 |
The web interface is slightly different from that generated by the |
409 |
<code>jscoverage</code> program: it has a new tab named "Store". |
410 |
To store coverage data, click the "Store" tab. |
411 |
</p> |
412 |
|
413 |
<img src="screenshot7.png" alt="Screenshot"> |
414 |
|
415 |
<p> |
416 |
When you click the "Store" button, the coverage data will be saved to a directory named <code>jscoverage-report/</code>. |
417 |
You can view this stored report at any time by opening the file <code>jscoverage-report/jscoverage.html</code> in |
418 |
your web browser - you don't need the <code>jscoverage-server</code> running to see it. |
419 |
</p> |
420 |
|
421 |
<p> |
422 |
If you use the "Store" tab again to store coverage data, the new data will be merged with |
423 |
the previous data in the <code>jscoverage-report/</code> directory. This can be useful, |
424 |
for instance, if you wish to run a set of tests in different browsers and generate an |
425 |
aggregate report which combines the data for all of them. |
426 |
</p> |
427 |
|
428 |
<p> |
429 |
You can also store data programmatically from your tests by adding the following to your |
430 |
JavaScript code: |
431 |
</p> |
432 |
|
433 |
<pre> |
434 |
if (top.jscoverage_report) { |
435 |
top.jscoverage_report(); |
436 |
} |
437 |
</pre> |
438 |
|
439 |
<p> |
440 |
You can stop the server by running another instance of <code>jscoverage-server</code> with the |
441 |
<code>--shutdown</code> option: |
442 |
</p> |
443 |
|
444 |
<pre> |
445 |
jscoverage-server --shutdown |
446 |
</pre> |
447 |
|
448 |
<h2>Using <code>jscoverage-server --proxy</code></h2> |
449 |
|
450 |
<p> |
451 |
To use <code>jscoverage-server</code> as a proxy server, use the <code>--proxy</code> option: |
452 |
</p> |
453 |
|
454 |
<pre> |
455 |
jscoverage-server --verbose --proxy |
456 |
</pre> |
457 |
|
458 |
<p> |
459 |
Configure your browser to use an HTTP proxy with address 127.0.0.1 and port 8080. |
460 |
You can then generate code coverage data for a web page on the server <code>example.com</code> |
461 |
by accessing the JSCoverage web interface at the special URL <code>http://example.com/jscoverage.html</code>. |
462 |
Note that this URL is not provided by the <code>example.com</code> server; it is automatically generated |
463 |
by the proxy server whenever a URL with path <code>/jscoverage.html</code> is requested. |
464 |
</p> |
465 |
|
466 |
<h2><code>jscoverage-server</code> command line options</h2> |
467 |
|
468 |
<dl> |
469 |
<dt><code>-h</code>, <code>--help</code> |
470 |
<dd>Display a brief help message. |
471 |
<dt><code>-V</code>, <code>--version</code> |
472 |
<dd>Display the version of the program. |
473 |
<dt><code>-v</code>, <code>--verbose</code> |
474 |
<dd>Explain what is being done. |
475 |
<dt><code>--document-root=<var>PATH</var></code> |
476 |
<dd>Serve web content from the directory given by <var>PATH</var>. The default is |
477 |
the current directory. This option may not be given with the <code>--proxy</code> option. |
478 |
<dt><code>--ip-address=<var>ADDRESS</var></code> |
479 |
<dd>Run the server on the IP address given by <var>ADDRESS</var>. The default is <code>127.0.0.1</code>. Specify |
480 |
<code>0.0.0.0</code> to use any address. |
481 |
<dt><code>--no-instrument=<var>URL</var></code> |
482 |
<dd>Do not instrument JavaScript code from <var>URL</var>. If you are running <code>jscoverage-server</code> |
483 |
with the <code>--proxy</code> option, <var>URL</var> should be a full URL. For example: |
484 |
<pre> |
485 |
jscoverage-server --proxy --no-instrument=http://example.com/scripts/ |
486 |
</pre> |
487 |
Without <code>--proxy</code>, <var>URL</var> should be only the path portion of a URL: |
488 |
<pre> |
489 |
jscoverage-server --no-instrument=/scripts/ |
490 |
</pre> |
491 |
This option may be given multiple times. |
492 |
<dt><code>--port=<var>PORT</var></code> |
493 |
<dd>Run the server on the port given by <var>PORT</var>. The default is port 8080. |
494 |
<dt><code>--proxy</code> |
495 |
<dd>Run as a proxy server. |
496 |
<dt><code>--report-dir=<var>PATH</var></code> |
497 |
<dd>Use the directory given by <var>PATH</var> for storing coverage reports. The default is |
498 |
<code>jscoverage-report/</code> in the current directory. |
499 |
<dt><code>--shutdown</code> |
500 |
<dd>Stop a running instance of the server. |
501 |
</dl> |
502 |
|
503 |
<h2>Caveats</h2> |
504 |
|
505 |
<ul> |
506 |
<li>JSCoverage adds instrumentation to JavaScript code, which will slow down execution speed. |
507 |
Expect instrumented code to take at least twice as much time to run. |
508 |
<li>JSCoverage currently instruments only <code>.js</code> files; it does not instrument code in <code><script></code> |
509 |
elements in HTML files. |
510 |
<li>HTML files must use relative URLs to reference scripts. If you use an absolute URL, your page will reference |
511 |
the original uninstrumented script rather than the instrumented one, and no code coverage data will be collected. |
512 |
<li>JSCoverage instruments physical lines of code rather than logical JavaScript statements; it works bests with code |
513 |
that has exactly one statement per line. If you put multiple statements on a line, or split a line across two or more |
514 |
statements, you may get strange results. |
515 |
<li>JSCoverage uses frames. Some web pages that use frames may not function properly when run under JSCoverage, especially |
516 |
those which try to access the top-level frame (<code>window.top</code>, <code>target="_top"</code>, etc.). |
517 |
<li>JSCoverage is alpha software. Use at your own risk. |
518 |
</ul> |
519 |
|
520 |
<address> |
521 |
Copyright © 2007, 2008 <a href="http://siliconforks.com/"><img src="http://siliconforks.com/siliconforks-16x16.png" width="16" height="16" class="icon" alt="Silicon Forks"></a> <a href="http://siliconforks.com/">siliconforks.com</a><br> |
522 |
Last updated May 21, 2008<br> |
523 |
<a href="mailto:jscoverage@siliconforks.com">jscoverage@siliconforks.com</a> |
524 |
</address> |
525 |
|
526 |
</body> |
527 |
</html> |