[TFUI] test-driven Ajax
Phlip <[email protected]> 08 Mar 2007 09:46:49 -0800
| Newsgroups | gmane.comp.programming.test-first-user-interfaces,gmane.comp.programming.test-driven-development,gmane.comp.lang.ruby.rails.spinoffs |
|---|---|
| Message-ID | <[email protected]> |
--DHetoUhe6-3y11i-qKOHJkgp-6ZrPCrcGGKljOC
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Adam Sroka wrote:
> ...Given that most web apps
> today are highly dependent on the Ajax stack I would not trust these
> frameworks unless they have *greatly* improved since the last time I
> looked at them.
If I can avoid my usual plagues and disasters for just a couple more
weeks now, I will _finally_ be publishing a chapter of /TFUI/.
(Woo-hoo! and _incredibly_ sorry about the wait, everyone!)
The chapter shows how to use Rails and Ajax to write a test runner, in
the style of Watir or Selenium. It uses Ajax to edit and maintain test
cases in a web page, and tests target pages in an IFrame.
The chapter shows how to build the core of this tool from Rails, its
existing test rigs, and some new test rigs I assemble on the fly. It
invents:
assert_xpath
assert_js
assert_ajax
Anyone proficient with Rails can sample the test runner itself here:
http://phlip.svnrepository.com/svn/yar_wiki/README
It currently beats on a couple of my Rails projects. If you install it
but don't build a controller for it, you get assert_xpath and
assert_js. (Only the wiki can do assert_ajax!)
Here's an extreme example of assert_xpath:
assert_xml x.target!
assert_xpath '/ul/li/table/tr/td/strong[ . = "test_hammy_squirrel" ]' do
assert_xpath '../../../../ul/li/table/tr/td/strong[ . = "script" ]' do
assert_xpath '../../../../div[ . = "# something tests hammy squirrel" ]'
end
end
The first assertion takes a string, parses it as XML, and puts it into
a secret member variable called @xdoc. The assert_xpath requires a
valid path, so you can put lots of query details into the path. Then
assert_xpath upgrades @xdoc to point to the selected node, and it
calls a block. assert_xpath inside the block then works on the
selected node. In the above example (which could be more ...
exemplary), the nested assert_xpath calls respond by reaching back up
the tree of nodes, and finding distant cousins of the selected nodes.
assert_js lexes intermediate JavaScript source into XML, so
assert_xpath can then query out its important details:
assert_xpath '/form/textarea' do |textarea|
assert_js textarea.attributes['onkeydown'] do
assert_xpath 'Statement[1]' do
assert_xpath '//Identifier[ @name = "Callee" and . =
"editor_keydown" ]'
end
end
end
That might be better than "assert_match /onkeydown.*editor_keydown/,
my_javascript". The match could break when irrelevant details change.
assert_ajax works by registering a JavaScript callback to file when an
Ajax command finishes. You can populate the callback with assertions
to check that your Ajax updated your DOM correctly.
--
Phlip
http://c2.com/cgi/wiki?ZeekLand <-- NOT a blog!!
--DHetoUhe6-3y11i-qKOHJkgp-6ZrPCrcGGKljOC
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
</head>
<!-- Network content -->
<body style="background-color: #ffffff;">
<!--~-|**|PrettyHtmlStartT|**|-~-->
<div id="ygrp-mlmsg" style="width:655px; position:relative;">
<div id="ygrp-msg" style="width: 490px; padding: 0 15px 0 0; float:left; z-index:1;">
<!--~-|**|PrettyHtmlEndT|**|-~-->
<div id="ygrp-text">
<p>Adam Sroka wrote:<br>
<br>
> ...Given that most web apps<br>
> today are highly dependent on the Ajax stack I would not trust these<br>
> frameworks unless they have *greatly* improved since the last time I<br>
> looked at them.<br>
<br>
If I can avoid my usual plagues and disasters for just a couple more<br>
weeks now, I will _finally_ be publishing a chapter of /TFUI/.<br>
(Woo-hoo! and _incredibly_ sorry about the wait, everyone!)<br>
<br>
The chapter shows how to use Rails and Ajax to write a test runner, in<br>
the style of Watir or Selenium. It uses Ajax to edit and maintain test<br>
cases in a web page, and tests target pages in an IFrame.<br>
<br>
The chapter shows how to build the core of this tool from Rails, its<br>
existing test rigs, and some new test rigs I assemble on the fly. It<br>
invents:<br>
<br>
assert_xpath<br>
assert_js<br>
assert_ajax<br>
<br>
Anyone proficient with Rails can sample the test runner itself here:<br>
<br>
<a href="http://phlip.svnrepository.com/svn/yar_wiki/README">http://phlip.<wbr>svnrepository.<wbr>com/svn/yar_<wbr>wiki/README</a><br>
<br>
It currently beats on a couple of my Rails projects. If you install it<br>
but don't build a controller for it, you get assert_xpath and<br>
assert_js. (Only the wiki can do assert_ajax!<wbr>)<br>
<br>
Here's an extreme example of assert_xpath:<br>
<br>
assert_xml x.target!<br>
<br>
assert_xpath '/ul/li/table/<wbr>tr/td/strong[ . = "test_hammy_<wbr>squirrel" ]' do<br>
assert_xpath '../../../..<wbr>/ul/li/table/<wbr>tr/td/strong[ . = "script" ]' do<br>
assert_xpath '../../../..<wbr>/div[ . = "# something tests hammy squirrel" ]'<br>
end<br>
end<br>
<br>
The first assertion takes a string, parses it as XML, and puts it into<br>
a secret member variable called @xdoc. The assert_xpath requires a<br>
valid path, so you can put lots of query details into the path. Then<br>
assert_xpath upgrades @xdoc to point to the selected node, and it<br>
calls a block. assert_xpath inside the block then works on the<br>
selected node. In the above example (which could be more ...<br>
exemplary), the nested assert_xpath calls respond by reaching back up<br>
the tree of nodes, and finding distant cousins of the selected nodes.<br>
<br>
assert_js lexes intermediate JavaScript source into XML, so<br>
assert_xpath can then query out its important details:<br>
<br>
assert_xpath '/form/textarea' do |textarea|<br>
assert_js textarea.attributes<wbr>['onkeydown'<wbr>] do<br>
assert_xpath 'Statement[1]<wbr>' do<br>
assert_xpath '//Identifier[ @name = "Callee" and . =<br>
"editor_keydown" ]'<br>
end<br>
end<br>
end<br>
<br>
That might be better than "assert_match /onkeydown.*<wbr>editor_keydown/<wbr>,<br>
my_javascript"<wbr>. The match could break when irrelevant details change.<br>
<br>
assert_ajax works by registering a JavaScript callback to file when an<br>
Ajax command finishes. You can populate the callback with assertions<br>
to check that your Ajax updated your DOM correctly.<br>
<br>
-- <br>
Phlip<br>
<a href="http://c2.com/cgi/wiki?ZeekLand">http://c2.com/<wbr>cgi/wiki?<wbr>ZeekLand</a> <-- NOT a blog!!<br>
</p>
</div>
<!--~-|**|PrettyHtmlStart|**|-~-->
<span width="1" style="color: white;">__._,_.___</span>
<!-- Start the section with Message In topic -->
<div id="ygrp-actbar">
<span class="left">
<a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/message/975;_ylc=X3oDMTMyb2llbzZtBF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BG1zZ0lkAzk3NQRzZWMDZnRyBHNsawN2dHBjBHN0aW1lAzExNzMzNzYwMDkEdHBjSWQDOTc1">
Messages in this topic </a> (<span class="bld">1</span>)
</span>
<a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/post;_ylc=X3oDMTJvNmIwY2ZvBF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BG1zZ0lkAzk3NQRzZWMDZnRyBHNsawNycGx5BHN0aW1lAzExNzMzNzYwMDk-?act=reply&messageNum=975">
<span class="bld">
Reply </span> (via web post)
</a> |
<a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/post;_ylc=X3oDMTJlbzEyZ3I1BF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA250cGMEc3RpbWUDMTE3MzM3NjAwOQ--" class="bld">
Start a new topic </a>
</div>
<!------- Start Nav Bar ------>
<!-- |**|begin egp html banner|**| -->
<div id="ygrp-vitnav">
<a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/messages;_ylc=X3oDMTJlMnBlYnUyBF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA21zZ3MEc3RpbWUDMTE3MzM3NjAwOQ--">Messages</a>
| <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/files;_ylc=X3oDMTJmYmhwbTA2BF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA2ZpbGVzBHN0aW1lAzExNzMzNzYwMDk-">Files</a>
| <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/photos;_ylc=X3oDMTJlZDlpYzg3BF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA3Bob3QEc3RpbWUDMTE3MzM3NjAwOQ--">Photos</a>
| <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/links;_ylc=X3oDMTJmdWUxa3ByBF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA2xpbmtzBHN0aW1lAzExNzMzNzYwMDk-">Links</a>
| <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/database;_ylc=X3oDMTJjdTQzNDNpBF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA2RiBHN0aW1lAzExNzMzNzYwMDk-">Database</a>
| <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/polls;_ylc=X3oDMTJma2I2M2I0BF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA3BvbGxzBHN0aW1lAzExNzMzNzYwMDk-">Polls</a>
| <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/members;_ylc=X3oDMTJlbjAzM2gwBF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA21icnMEc3RpbWUDMTE3MzM3NjAwOQ--">Members</a>
| <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/calendar;_ylc=X3oDMTJkNWl2ZDEwBF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA2NhbARzdGltZQMxMTczMzc2MDA5">Calendar</a>
</div>
<!-- |**|end egp html banner|**| -->
<!-- Do not have place to put it yet -->
<div id="ygrp-grft">
</div>
<!-- yahoo logo -->
<!-- |**|begin egp html banner|**| -->
<div id="ygrp-ft">
<a href="http://groups.yahoo.com/;_ylc=X3oDMTJkYWhxb3BrBF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA2dmcARzdGltZQMxMTczMzc2MDA5">
<img src="http://us.i1.yimg.com/us.yimg.com/i/yg/img/logo/ma_grp_160.gif" height="15" width="106" border="0" alt="Yahoo! Groups"></a> <br>
<a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/join;_ylc=X3oDMTJmOTUwZmw4BF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA3N0bmdzBHN0aW1lAzExNzMzNzYwMDk-">Change settings via the Web</a> (Yahoo! ID required) <br>
Change settings via email: <a href="mailto:TestFirstUserInterfaces-digest-hHKSG33TihhbjbujkaE4pw@public.gmane.org?subject=Email Delivery: Digest">Switch delivery to Daily Digest</a> | <a href = "mailto:TestFirstUserInterfaces-traditional-hHKSG33TihhbjbujkaE4pw@public.gmane.org?subject=Change Delivery Format: Traditional">Switch format to Traditional</a> <br>
<a href="http://groups.yahoo.com/group/TestFirstUserInterfaces;_ylc=X3oDMTJkMnNuc2g2BF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA2hwZgRzdGltZQMxMTczMzc2MDA5">
Visit Your Group
</a> |
<a href="http://docs.yahoo.com/info/terms/">
Yahoo! Groups Terms of Use </a> |
<a href="mailto:TestFirstUserInterfaces-unsubscribe-hHKSG33TihhbjbujkaE4pw@public.gmane.org?subject=">
Unsubscribe </a>
</div> <!-- |**|end egp html banner|**| -->
</div> <!-- ygrp-msg -->
<!-- Sponsor -->
<!-- |**|begin egp html banner|**| -->
<div id="ygrp-sponsor" style="width:140px;float: left; clear: none; margin-left: 5px; background:white; margin-bottom:25px ;position:absolute; top:0; right: 0;">
<!-- Network content -->
<!-- Start vitality -->
<div id="ygrp-vital">
<div id="vithd">Recent Activity</div>
<ul style="list-style-type:none; padding: 0; margin: 2px 0;">
<li style="clear: both;">
<div class="ct" style="float: right;"><span style="display:none"> </span>5</div>
<div class="cat"><a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/members;_ylc=X3oDMTJmampybzUzBF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwN2dGwEc2xrA3ZtYnJzBHN0aW1lAzExNzMzNzYwMDk-">New Members</a></div>
</li>
</ul>
<a href="http://groups.yahoo.com/group/TestFirstUserInterfaces;_ylc=X3oDMTJlODFjNmFlBF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwN2dGwEc2xrA3ZnaHAEc3RpbWUDMTE3MzM3NjAwOQ--">
Visit Your Group </a>
</div>
<div id="hd">SPONSORED LINKS</div>
<div id="ov">
<ul>
<li><a href="http://groups.yahoo.com/gads;_ylc=X3oDMTJjczc4bDFlBF9TAzk3MzU5NzE0BF9wAzEEZ3JwSWQDODc0NDcwNARncnBzcElkAzE3MDUwMDcyMDcEc2VjA3NsbW9kBHN0aW1lAzExNzMzNzYwMDk-?t=ms&k=Software+development+tool&w1=Software+development+tool&w2=Application+development+software&w3=Development+software&w4=Development+outsourcing+software&w5=Embedded+software+development&c=5&s=168&g=2&.sig=dMdO9VHGmvW_YJCzvobcVA">Software development tool</a></li>
<li><a href="http://groups.yahoo.com/gads;_ylc=X3oDMTJjaWE1MXEwBF9TAzk3MzU5NzE0BF9wAzIEZ3JwSWQDODc0NDcwNARncnBzcElkAzE3MDUwMDcyMDcEc2VjA3NsbW9kBHN0aW1lAzExNzMzNzYwMDk-?t=ms&k=Application+development+software&w1=Software+development+tool&w2=Application+development+software&w3=Development+software&w4=Development+outsourcing+software&w5=Embedded+software+development&c=5&s=168&g=2&.sig=L7Zs-h76VVahdNyx1QnUHg">Application development software</a></li>
<li><a href="http://groups.yahoo.com/gads;_ylc=X3oDMTJjMGVmOGs0BF9TAzk3MzU5NzE0BF9wAzMEZ3JwSWQDODc0NDcwNARncnBzcElkAzE3MDUwMDcyMDcEc2VjA3NsbW9kBHN0aW1lAzExNzMzNzYwMDk-?t=ms&k=Development+software&w1=Software+development+tool&w2=Application+development+software&w3=Development+software&w4=Development+outsourcing+software&w5=Embedded+software+development&c=5&s=168&g=2&.sig=mgzP-_wxW4LP9qRGd-WceA">Development software</a></li>
<li><a href="http://groups.yahoo.com/gads;_ylc=X3oDMTJjNGlxNGE4BF9TAzk3MzU5NzE0BF9wAzQEZ3JwSWQDODc0NDcwNARncnBzcElkAzE3MDUwMDcyMDcEc2VjA3NsbW9kBHN0aW1lAzExNzMzNzYwMDk-?t=ms&k=Development+outsourcing+software&w1=Software+development+tool&w2=Application+development+software&w3=Development+software&w4=Development+outsourcing+software&w5=Embedded+software+development&c=5&s=168&g=2&.sig=To5fERSOzD6F-d_wLzNE1w">Development outsourcing software</a></li>
<li><a href="http://groups.yahoo.com/gads;_ylc=X3oDMTJjcTI5OWtoBF9TAzk3MzU5NzE0BF9wAzUEZ3JwSWQDODc0NDcwNARncnBzcElkAzE3MDUwMDcyMDcEc2VjA3NsbW9kBHN0aW1lAzExNzMzNzYwMDk-?t=ms&k=Embedded+software+development&w1=Software+development+tool&w2=Application+development+software&w3=Development+software&w4=Development+outsourcing+software&w5=Embedded+software+development&c=5&s=168&g=2&.sig=uSsL2YUoig8RH1hp4MMhQg">Embedded software development</a></li>
</ul>
</div>
<!-- Network content -->
<div id="nc">
<div class="ad">
<div id="hd1">Cool Websites</div>
<p><a href="http://us.ard.yahoo.com/SIG=12j1imi7k/M=493064.10322723.10977639.9706567/D=groups/S=1705007207:NC/Y=YAHOO/EXP=1173383209/A=4438988/R=0/SIG=111nufaaj/*http://www.bix.com/contest/4746">Know a good site?</a></p>
<p>Share and vote</p>
<p>on Bix.com!</p> </div>
<div class="ad">
<div id="hd1">Ads on Yahoo!</div>
<p><a href="http://us.ard.yahoo.com/SIG=12ifo39pa/M=493064.9803227.10510220.8674578/D=groups/S=1705007207:NC/Y=YAHOO/EXP=1173383209/A=3848643/R=0/SIG=131q47hek/*http://searchmarketing.yahoo.com/arp/srchv2.php?o=US2005&cmp=Yahoo&ctv=Groups4&s=Y&s2=&s3=&b=50">Learn more now.</a></p>
<p>Reach customers</p>
<p>searching for you.</p> </div>
<div class="ad">
<div id="hd1">Yahoo! Groups</div>
<p><a href="http://groups.yahoo.com/start;_ylc=X3oDMTJvNXYzOGppBF9TAzk3MzU5NzE0BF9wAzMEZ3JwSWQDODc0NDcwNARncnBzcElkAzE3MDUwMDcyMDcEc2VjA25jbW9kBHNsawNncm91cHMyBHN0aW1lAzExNzMzNzYwMDk-">Start a group</a></p>
<p>in 3 easy steps.</p>
<p>Connect with others.</p>
</div>
</div>
</div> <!-- |**|end egp html banner|**| -->
<div style="clear:both; color: #FFF; font-size:1px;">.</div>
</div> <img src="http://geo.yahoo.com/serv?s=97359714/grpId=8744704/grpspId=1705007207/msgId=975/stime=1173376009/nc1=4438988/nc2=3848643/nc3=3" width="1" height="1"> <br>
<span style="color: white;">__,_._,___</span>
<!--~-|**|PrettyHtmlEnd|**|-~-->
</body>
<!--~-|**|PrettyHtmlStart|**|-~-->
<head>
<style type="text/css">
<!--
#ygrp-mlmsg {font-size:13px; font-family: arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}
#ygrp-mlmsg table {font-size:inherit;font:100%;}
#ygrp-mlmsg select, input, textarea {font:99% arial,helvetica,clean,sans-serif;}
#ygrp-mlmsg pre, code {font:115% monospace;*font-size:100%;}
#ygrp-mlmsg * {line-height:1.22em;}
#ygrp-text{
font-family: Georgia;
}
#ygrp-text p{
margin: 0 0 1em 0;
}
#ygrp-tpmsgs{
font-family: Arial;
clear: both;
}
#ygrp-vitnav{
padding-top: 10px;
font-family: Verdana;
font-size: 77%;
margin: 0;
}
#ygrp-vitnav a{
padding: 0 1px;
}
#ygrp-actbar{
clear: both;
margin: 25px 0;
white-space:nowrap;
color: #666;
text-align: right;
}
#ygrp-actbar .left{
float: left;
white-space:nowrap;
}
.bld{font-weight:bold;}
#ygrp-grft{
font-family: Verdana;
font-size: 77%;
padding: 15px 0;
}
#ygrp-ft{
font-family: verdana;
font-size: 77%;
border-top: 1px solid #666;
padding: 5px 0;
}
#ygrp-mlmsg #logo{
padding-bottom: 10px;
}
#ygrp-vital{
background-color: #e0ecee;
margin-bottom: 20px;
padding: 2px 0 8px 8px;
}
#ygrp-vital #vithd{
font-size: 77%;
font-family: Verdana;
font-weight: bold;
color: #333;
text-transform: uppercase;
}
#ygrp-vital ul{
padding: 0;
margin: 2px 0;
}
#ygrp-vital ul li{
list-style-type: none;
clear: both;
border: 1px solid #e0ecee;
}
#ygrp-vital ul li .ct{
font-weight: bold;
color: #ff7900;
float: right;
width: 2em;
text-align:right;
padding-right: .5em;
}
#ygrp-vital ul li .cat{
font-weight: bold;
}
#ygrp-vital a {
text-decoration: none;
}
#ygrp-vital a:hover{
text-decoration: underline;
}
#ygrp-sponsor #hd{
color: #999;
font-size: 77%;
}
#ygrp-sponsor #ov{
padding: 6px 13px;
background-color: #e0ecee;
margin-bottom: 20px;
}
#ygrp-sponsor #ov ul{
padding: 0 0 0 8px;
margin: 0;
}
#ygrp-sponsor #ov li{
list-style-type: square;
padding: 6px 0;
font-size: 77%;
}
#ygrp-sponsor #ov li a{
text-decoration: none;
font-size: 130%;
}
#ygrp-sponsor #nc {
background-color: #eee;
margin-bottom: 20px;
padding: 0 8px;
}
#ygrp-sponsor .ad{
padding: 8px 0;
}
#ygrp-sponsor .ad #hd1{
font-family: Arial;
font-weight: bold;
color: #628c2a;
font-size: 100%;
line-height: 122%;
}
#ygrp-sponsor .ad a{
text-decoration: none;
}
#ygrp-sponsor .ad a:hover{
text-decoration: underline;
}
#ygrp-sponsor .ad p{
margin: 0;
}
o {font-size: 0; }
.MsoNormal {
margin: 0 0 0 0;
}
#ygrp-text tt{
font-size: 120%;
}
blockquote{margin: 0 0 0 4px;}
.replbq {margin:4}
-->
</style>
</head>
<!--~-|**|PrettyHtmlEnd|**|-~-->
</html><!--End group email -->
--DHetoUhe6-3y11i-qKOHJkgp-6ZrPCrcGGKljOC--