Developing and testing Ajax components Ignacio Coloma [email_address] http://extrema-sistemas.com
The goal Develop  and  test javascript components
Why? We usually get  lost in the details integrating Ajax with java
Two types of web applications RIA  vs  REST (You should choose)
REST misuse This is  not  an iPhone
RIA misuse This is  not  a knife
Don't forget the search engine! You may call it  SEO , We call it
REST and unobtrusive javascript Old: <input onchange=”something()”> New: <input id=”price”> <script> $('price').observe('change', something); </script>
Progressive enhancement Design  for javascript-less Add enhancements  if javascript is present.
User-perceived performance CSS at the top downloaded in parallel JS at the bottom downloaded sequentially <html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;> <head> <title>Loom Demo - </title> <link href=&quot;/loomdemo/css/yaml-3.0.5/core/base.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;></link> <link href=&quot;/loomdemo/css/yaml-3.0.5/navigation/nav_vlist.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;></link> <link href=&quot;/loomdemo/css/yaml-3.0.5/navigation/nav_slidingdoor.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;></link> <link href=&quot;/loomdemo/css/yaml-3.0.5/screen/content_default.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;></link> <link href=&quot;/loomdemo/css/yaml-3.0.5/core/print_base.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;></link> <!--[if IE]><link href=&quot;/loomdemo/css/yaml-3.0.5/core/iehacks.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;></link><![endif]--> </head> <body> <div id=&quot;page_margins&quot;> <div id=&quot;page&quot;> <div id=&quot;header&quot;> <div id=&quot;topleft&quot;> <a href=&quot;/loomdemo/&quot; title=&quot;Demo home&quot;>Demo home</a> | <a href=&quot;http://loom.extrema-sistemas.org/&quot; title=&quot;Loom home&quot;>Loom home</a> | <a href=&quot;http://loom.extrema-sistemas.org/get-documentation&quot; title=&quot;Loom doc&quot;>Loom doc</a>  </div> <div id=&quot;topcenter&quot;> Change language: ( <a href=&quot;/loomdemo/support/Locale.action?locale=en&quot; hreflang=&quot;en&quot; class=&quot;selected&quot;>english</a> | <a href=&quot;/loomdemo/support/Locale.action?locale=es&quot; hreflang=&quot;es&quot; class=&quot;&quot;>español</a> ) </div> <div id=&quot;topnav&quot;> <a href=&quot;/loomdemo/other/ViewJspSource.action?jspFilename=/WEB-INF/jsp/ebanking/listAll.jsp&actionName=org.loom.demo.action.ebanking.MortgagesAction&quot; class=&quot;strong&quot; title=&quot;View source&quot;>View source</a> </div> </div>   <div id=&quot;main&quot;> < <div id=&quot;col2&quot;> <div id=&quot;col2_content&quot; class=&quot;clearfix&quot;> <div id=&quot;tips&quot;>   <div class=&quot;hide-fouc important&quot;>Tips are only available with javascript turned on</div> </div> </div> </div> <div id=&quot;col3&quot;> <div id=&quot;col3_content&quot; class=&quot;clearfix&quot;> <form action=&quot;/loomdemo/ebanking/Mortgages.action&quot;> <div class=&quot;buttonBar&quot;> <input type=&quot;submit&quot; value=&quot;Delete&quot; class=&quot;submit&quot; /> </div> <table id=&quot;row&quot;> <thead> <tr> <th class=&quot;first&quot;></th> <th class=&quot;sortable&quot;> <a href=&quot;Mortgages.action?sort=id&amp;event=listAll&amp;dir=asc&quot;>ID</a></th> <th class=&quot;name sortable&quot;> <a href=&quot;Mortgages.action?sort=name&amp;event=listAll&amp;dir=asc&quot;>Name</a></th> <th class=&quot;sortable&quot;> <a href=&quot;Mortgages.action?sort=address&amp;event=listAll&amp;dir=asc&quot;>Address</a></th> <th class=&quot;date sortable&quot;> <a href=&quot;Mortgages.action?sort=principalLoanBalance&amp;event=listAll&amp;dir=asc&quot;>Loan</a></th> <th></th> <th class=&quot;date sortable&quot;> <a href=&quot;Mortgages.action?sort=creationDate&amp;event=listAll&amp;dir=asc&quot;>Creation date</a></th></tr></thead> <tbody> <tr class=&quot;empty&quot;><td colspan=&quot;7&quot;>Nothing found to display.</td></tr> </tbody> </table> <input type=&quot;hidden&quot; name=&quot;__source&quot; value=&quot;/WEB-INF/jsp/ebanking/listAll.jsp&quot;/> <input type=&quot;hidden&quot; name=&quot;event&quot; value=&quot;delete&quot;/> </form> <!-- IE Column Clearing -->  <div id=&quot;ie_clearing&quot;> &#160; </div>  </div> </div> </div> <div id=&quot;footer&quot;> <p style=&quot;float:right&quot;>Layout based on <a href=&quot;http://www.yaml.de/en/home.html&quot;>YAML</a></p>  <p>You are currently one of the 1 users browsing this demo site.  -  <a href=&quot;/loomdemo/about.jsp&quot; title=&quot;about this demo site&quot;>about this demo site</a></p> <p>&copy; 2007 Extrema Sistemas de Informaci&oacute;n - <a href=&quot;http://extrema-sistemas.com/en/legal&quot; title=&quot;Terms of use&quot;>Terms of use</a> -  <a href=&quot;http://extrema-sistemas.com/en/contact&quot; title=&quot;Go to the contact form&quot;>Contact</a> -  <a href=&quot;http://extrema-sistemas.com&quot; title=&quot;Go to Extrema home&quot;>Extrema home</a> </p> </div>   </div> </div> <script src=&quot;//ajax.googleapis.com/ajax/libs/prototype/1.6.0.2/prototype.js&quot; type=&quot;text/javascript&quot;></script> <script src=&quot;/loomdemo/js/loom-1.0-SNAPSHOT/loom.js&quot; type=&quot;text/javascript&quot;></script> <script src=&quot;//ajax.googleapis.com/ajax/libs/scriptaculous/1.8.1/scriptaculous.js?load=builder,effects&quot; type=&quot;text/javascript&quot;>  </script> <script src=&quot;/loomdemo/js/tips.js&quot; type=&quot;text/javascript&quot;></script> </body> </html>
Custom JSP tag Write  HTML
Connect  javascript autocompleter <myapp:autocompleter  name=”parent” action=&quot;Entries&quot; event=&quot;getParentCandidates&quot; />
First attempt The server generates  HTML + javascript: <input type=”text”  name=”parent” id=”parent” /> <script> new Autocompleter($(“parent”), { url: “http://localhost/getCandidates.json” }) </script>
Problems The id attribute is  required  or must be  generated  (fluff)
The javascript code is  generated by the server   (inflexible)
Some libraries  must be loaded  before the HTML  (really bad for performance)
Too many <script> tags  (harder to debug and hurts performance)
Better solution Generate just the  HTML code : <input type=”text”  name=”parent”  class=”autocompleter” url=“http://localhost/getCandidates.json” />
Add static javascript code Add javascript code as needed
This is  your javascript code , not generated by the server: $$('input.autocompleter').each(function(e) { new Autocompleter(e, { url: e.getAttribute('url'); }); });
Benefits The id attribute is  not required
All javascript code is together  at the end of the page  or inside  external javascript files
Maximum  javascript flexibility
Cleaner code that is  cacheable
Conclusion: HTML as the API HTML is the contract between your  java server  and your  javascript code .
Test your code Javascript code  should also be tested
Three ways of testing GWT  – java code translated to javascript
Java-driven tests:  WebDriver, Selenium
Javascript-driven tests
Broad choice JQuery : Qunit
Prototype : UnitTest

Developing and testing ajax components

  • 1.
    Developing and testingAjax components Ignacio Coloma [email_address] http://extrema-sistemas.com
  • 2.
    The goal Develop and test javascript components
  • 3.
    Why? We usuallyget lost in the details integrating Ajax with java
  • 4.
    Two types ofweb applications RIA vs REST (You should choose)
  • 5.
    REST misuse Thisis not an iPhone
  • 6.
    RIA misuse Thisis not a knife
  • 7.
    Don't forget thesearch engine! You may call it SEO , We call it
  • 8.
    REST and unobtrusivejavascript Old: <input onchange=”something()”> New: <input id=”price”> <script> $('price').observe('change', something); </script>
  • 9.
    Progressive enhancement Design for javascript-less Add enhancements if javascript is present.
  • 10.
    User-perceived performance CSSat the top downloaded in parallel JS at the bottom downloaded sequentially <html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;> <head> <title>Loom Demo - </title> <link href=&quot;/loomdemo/css/yaml-3.0.5/core/base.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;></link> <link href=&quot;/loomdemo/css/yaml-3.0.5/navigation/nav_vlist.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;></link> <link href=&quot;/loomdemo/css/yaml-3.0.5/navigation/nav_slidingdoor.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;></link> <link href=&quot;/loomdemo/css/yaml-3.0.5/screen/content_default.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;></link> <link href=&quot;/loomdemo/css/yaml-3.0.5/core/print_base.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;></link> <!--[if IE]><link href=&quot;/loomdemo/css/yaml-3.0.5/core/iehacks.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;></link><![endif]--> </head> <body> <div id=&quot;page_margins&quot;> <div id=&quot;page&quot;> <div id=&quot;header&quot;> <div id=&quot;topleft&quot;> <a href=&quot;/loomdemo/&quot; title=&quot;Demo home&quot;>Demo home</a> | <a href=&quot;http://loom.extrema-sistemas.org/&quot; title=&quot;Loom home&quot;>Loom home</a> | <a href=&quot;http://loom.extrema-sistemas.org/get-documentation&quot; title=&quot;Loom doc&quot;>Loom doc</a> </div> <div id=&quot;topcenter&quot;> Change language: ( <a href=&quot;/loomdemo/support/Locale.action?locale=en&quot; hreflang=&quot;en&quot; class=&quot;selected&quot;>english</a> | <a href=&quot;/loomdemo/support/Locale.action?locale=es&quot; hreflang=&quot;es&quot; class=&quot;&quot;>español</a> ) </div> <div id=&quot;topnav&quot;> <a href=&quot;/loomdemo/other/ViewJspSource.action?jspFilename=/WEB-INF/jsp/ebanking/listAll.jsp&actionName=org.loom.demo.action.ebanking.MortgagesAction&quot; class=&quot;strong&quot; title=&quot;View source&quot;>View source</a> </div> </div> <div id=&quot;main&quot;> < <div id=&quot;col2&quot;> <div id=&quot;col2_content&quot; class=&quot;clearfix&quot;> <div id=&quot;tips&quot;> <div class=&quot;hide-fouc important&quot;>Tips are only available with javascript turned on</div> </div> </div> </div> <div id=&quot;col3&quot;> <div id=&quot;col3_content&quot; class=&quot;clearfix&quot;> <form action=&quot;/loomdemo/ebanking/Mortgages.action&quot;> <div class=&quot;buttonBar&quot;> <input type=&quot;submit&quot; value=&quot;Delete&quot; class=&quot;submit&quot; /> </div> <table id=&quot;row&quot;> <thead> <tr> <th class=&quot;first&quot;></th> <th class=&quot;sortable&quot;> <a href=&quot;Mortgages.action?sort=id&amp;event=listAll&amp;dir=asc&quot;>ID</a></th> <th class=&quot;name sortable&quot;> <a href=&quot;Mortgages.action?sort=name&amp;event=listAll&amp;dir=asc&quot;>Name</a></th> <th class=&quot;sortable&quot;> <a href=&quot;Mortgages.action?sort=address&amp;event=listAll&amp;dir=asc&quot;>Address</a></th> <th class=&quot;date sortable&quot;> <a href=&quot;Mortgages.action?sort=principalLoanBalance&amp;event=listAll&amp;dir=asc&quot;>Loan</a></th> <th></th> <th class=&quot;date sortable&quot;> <a href=&quot;Mortgages.action?sort=creationDate&amp;event=listAll&amp;dir=asc&quot;>Creation date</a></th></tr></thead> <tbody> <tr class=&quot;empty&quot;><td colspan=&quot;7&quot;>Nothing found to display.</td></tr> </tbody> </table> <input type=&quot;hidden&quot; name=&quot;__source&quot; value=&quot;/WEB-INF/jsp/ebanking/listAll.jsp&quot;/> <input type=&quot;hidden&quot; name=&quot;event&quot; value=&quot;delete&quot;/> </form> <!-- IE Column Clearing --> <div id=&quot;ie_clearing&quot;> &#160; </div> </div> </div> </div> <div id=&quot;footer&quot;> <p style=&quot;float:right&quot;>Layout based on <a href=&quot;http://www.yaml.de/en/home.html&quot;>YAML</a></p> <p>You are currently one of the 1 users browsing this demo site. - <a href=&quot;/loomdemo/about.jsp&quot; title=&quot;about this demo site&quot;>about this demo site</a></p> <p>&copy; 2007 Extrema Sistemas de Informaci&oacute;n - <a href=&quot;http://extrema-sistemas.com/en/legal&quot; title=&quot;Terms of use&quot;>Terms of use</a> - <a href=&quot;http://extrema-sistemas.com/en/contact&quot; title=&quot;Go to the contact form&quot;>Contact</a> - <a href=&quot;http://extrema-sistemas.com&quot; title=&quot;Go to Extrema home&quot;>Extrema home</a> </p> </div> </div> </div> <script src=&quot;//ajax.googleapis.com/ajax/libs/prototype/1.6.0.2/prototype.js&quot; type=&quot;text/javascript&quot;></script> <script src=&quot;/loomdemo/js/loom-1.0-SNAPSHOT/loom.js&quot; type=&quot;text/javascript&quot;></script> <script src=&quot;//ajax.googleapis.com/ajax/libs/scriptaculous/1.8.1/scriptaculous.js?load=builder,effects&quot; type=&quot;text/javascript&quot;> </script> <script src=&quot;/loomdemo/js/tips.js&quot; type=&quot;text/javascript&quot;></script> </body> </html>
  • 11.
    Custom JSP tagWrite HTML
  • 12.
    Connect javascriptautocompleter <myapp:autocompleter name=”parent” action=&quot;Entries&quot; event=&quot;getParentCandidates&quot; />
  • 13.
    First attempt Theserver generates HTML + javascript: <input type=”text” name=”parent” id=”parent” /> <script> new Autocompleter($(“parent”), { url: “http://localhost/getCandidates.json” }) </script>
  • 14.
    Problems The idattribute is required or must be generated (fluff)
  • 15.
    The javascript codeis generated by the server (inflexible)
  • 16.
    Some libraries must be loaded before the HTML (really bad for performance)
  • 17.
    Too many <script>tags (harder to debug and hurts performance)
  • 18.
    Better solution Generatejust the HTML code : <input type=”text” name=”parent” class=”autocompleter” url=“http://localhost/getCandidates.json” />
  • 19.
    Add static javascriptcode Add javascript code as needed
  • 20.
    This is your javascript code , not generated by the server: $$('input.autocompleter').each(function(e) { new Autocompleter(e, { url: e.getAttribute('url'); }); });
  • 21.
    Benefits The idattribute is not required
  • 22.
    All javascript codeis together at the end of the page or inside external javascript files
  • 23.
    Maximum javascriptflexibility
  • 24.
    Cleaner code thatis cacheable
  • 25.
    Conclusion: HTML asthe API HTML is the contract between your java server and your javascript code .
  • 26.
    Test your codeJavascript code should also be tested
  • 27.
    Three ways oftesting GWT – java code translated to javascript
  • 28.
    Java-driven tests: WebDriver, Selenium
  • 29.
  • 30.
  • 31.
  • 32.
    Dojo : D.O.H(Dojo Objective Harness)
  • 33.
    MooTools : ClientcideTest Framework
  • 34.
  • 35.
    Others : FireUnit,TestMonkey...
  • 36.
    Javascript-driven tests 1:Static HTML page with javascript (manual test)
  • 37.
    2: Add javascript tests
  • 38.
    3: Add test suite
  • 39.
    1: Static HTMLpage <html> <body> <input type=”text” name=”parent” id=”foo” class=”autocompleter” url=“mock-candidates.json” /> <script> var auto = new Autocompleter($('foo'), { url: e.getAttribute('url'); }); </script> </body> </html>
  • 40.
    Testing with IEAdd the mark of the web to bypass the IE security warning <!DOCTYPE> <!-- saved from url=(0014)about:internet --> <html>
  • 41.
    Testing with IE7+ Enable XHR access to the local filesystem using IE 7 if (Prototype.Browser.IE && XMLHttpRequest) { XMLHttpRequest = function() { return new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;); } }
  • 42.
    2: Add javascripttests Identify functions that can be tested easily: var test = new Test.Unit.Runner({ testFooBar: function() { this.assertVisible(auto.element); this.assertEqual(120, auto.element.getWidth()); this.assertEqual('foo', auto.FooBar()); } });
  • 43.
    Make synchronous Ajaxcalls Add a flag to disable asynchronous XHR in your components var tabs = new loom.ui.Tabs($('tabs'), { ajaxOptions: { asynchronous: false } }); new Test.Unit.Runner({ testSelectAjaxTab: function() { tabs.selectTab($('a4')); this.assertEqual('foo', $('tab4').innerHTML.toLowerCase()); } });
  • 44.
    Make your testwait() Invoking window.settimeout() will not work! testThrottle: function() { var total = 0; var func = function(v) { total += v; }.throttle(1); for (var i = 0; i < 10; i++) { func(10); } wait(1000, function() { this.assertEqual(10, total); }.bind(this)); }
  • 45.
    3: Add testsuite Supported by some test frameworks
  • 46.
    Open allsupported browsers for the host environment
  • 47.
    Launch allthe configured test pages
  • 48.
    Inform about the results
  • 49.
    Integration tests Onceall static tests have passed, you should test integration with the server .
  • 50.
    Just confirm that the server generates the expected HTML contract .
  • 51.
    Testing Flash components1: Test HTML + flash + mock javascript
  • 52.
    2: Test HTML + flash + real javascript
  • 53.
    3: Integration test
  • 54.
    Thanks Ignacio Coloma[email_address] http://icoloma.blogspot.com http://extrema-sistemas.com

Editor's Notes

  • #6 This is the typical look of an application that starts adding a couple of js features and ends up like the Loch Ness Monster. Ajax is not like XP, it should not be improvised.
  • #8 Cuando se elige entre REST y RIA, tener en cuenta que a una aplicación en intranet le da igual estar indexada (y otros casos como Google wave,por ser contenidos de corta caducidad), pero al resto frecuentemente le hace falta indexarse. Hay un museo en Madrid que tiene en torno al 50% de accesos desde google. Para nosotros es el 75%.
  • #16 Lo puedo poner en una pagina o en un script general
  • #23 Puedo probar manualmente con firebug Puedo modificar lo que pruebo Puedo inspeccionarlo todo Uso un fichero local