<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4151123446707233521</id><updated>2011-07-30T07:36:03.580-07:00</updated><category term='it'/><category term='logging'/><category term='grails'/><category term='calendar'/><category term='griffon'/><category term='i18n'/><category term='internationalization'/><category term='sf4j'/><category term='testng'/><category term='java'/><category term='groovy'/><category term='swing'/><category term='stringtomap'/><category term='start'/><category term='programming'/><category term='logback'/><category term='testing'/><category term='webtest'/><category term='bubbles'/><title type='text'>IT Bubbles</title><subtitle type='html'>Blog related to programming and IT encountered in everyday life.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://it-bubbles.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4151123446707233521/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://it-bubbles.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Bubbles</name><uri>http://www.blogger.com/profile/06938554422574447792</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>8</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4151123446707233521.post-708264369681505347</id><published>2009-11-29T12:14:00.000-08:00</published><updated>2009-11-29T12:15:44.442-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sf4j'/><category scheme='http://www.blogger.com/atom/ns#' term='logging'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='logback'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><category scheme='http://www.blogger.com/atom/ns#' term='griffon'/><title type='text'>Griffon And org.slf4j (Logback)</title><content type='html'>See more information in following &lt;a href="http://it-bubbles.wikidot.com/blog:3"&gt;post&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4151123446707233521-708264369681505347?l=it-bubbles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://it-bubbles.blogspot.com/feeds/708264369681505347/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4151123446707233521&amp;postID=708264369681505347' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4151123446707233521/posts/default/708264369681505347'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4151123446707233521/posts/default/708264369681505347'/><link rel='alternate' type='text/html' href='http://it-bubbles.blogspot.com/2009/11/griffon-and-orgslf4j-logback.html' title='Griffon And org.slf4j (Logback)'/><author><name>Bubbles</name><uri>http://www.blogger.com/profile/06938554422574447792</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4151123446707233521.post-7939213221961841577</id><published>2009-11-23T14:15:00.000-08:00</published><updated>2009-11-25T12:43:37.082-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='internationalization'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='i18n'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><category scheme='http://www.blogger.com/atom/ns#' term='griffon'/><title type='text'>Internationalization in Griffon using stringtomap</title><content type='html'>Read more &lt;a href="http://it-bubbles.wikidot.com/blog:2"&gt;here.&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4151123446707233521-7939213221961841577?l=it-bubbles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://it-bubbles.blogspot.com/feeds/7939213221961841577/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4151123446707233521&amp;postID=7939213221961841577' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4151123446707233521/posts/default/7939213221961841577'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4151123446707233521/posts/default/7939213221961841577'/><link rel='alternate' type='text/html' href='http://it-bubbles.blogspot.com/2009/11/internationalization-in-griffon-using.html' title='Internationalization in Griffon using stringtomap'/><author><name>Bubbles</name><uri>http://www.blogger.com/profile/06938554422574447792</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4151123446707233521.post-5155383129265975364</id><published>2009-11-22T13:01:00.000-08:00</published><updated>2009-11-22T13:10:40.371-08:00</updated><title type='text'>Blogspot vs Wikidot</title><content type='html'>I always thought it would be fun and easy to write posts here on blogspot. What can be easier, you can even use google doc to prepare your posts and than publish it directly to blogspot. This may be true, but not for posts where you have source code. I know many bloggers use blogspot and can manage to write posts with source code examples. But for me it turns out I spend more time editing and adjusting  formatting the post than writing it.&lt;br /&gt;&lt;br /&gt;From now I will place posts with source code to &lt;a href="http://it-bubbles.wikidot.com/blog"&gt;http://it-bubbles.wikidot.com/blog&lt;/a&gt;, which seems more programmers friendly. Wikidot is Wiki based and even though it does not have such popularity as blogspot, I find it much easier to use. I'll try to keep references to posts here at blogspot as well.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4151123446707233521-5155383129265975364?l=it-bubbles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://it-bubbles.blogspot.com/feeds/5155383129265975364/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4151123446707233521&amp;postID=5155383129265975364' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4151123446707233521/posts/default/5155383129265975364'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4151123446707233521/posts/default/5155383129265975364'/><link rel='alternate' type='text/html' href='http://it-bubbles.blogspot.com/2009/11/blogspot-vs-wikidot.html' title='Blogspot vs Wikidot'/><author><name>Bubbles</name><uri>http://www.blogger.com/profile/06938554422574447792</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4151123446707233521.post-6567557616424679371</id><published>2009-05-26T12:40:00.001-07:00</published><updated>2009-05-28T14:46:53.780-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='webtest'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='grails'/><title type='text'>Test different language locale and browser type with Grails WebTest plugin.</title><content type='html'>&lt;span style="font-family:Arial;"&gt;&lt;br /&gt;Grails &lt;/span&gt;&lt;a style="font-family: Courier New;" title="WebTest plugin" href="http://grails.org/plugin/webtest" id="rb7l"&gt;WebTest plugin&lt;/a&gt;&lt;span style="font-family:Arial;"&gt; allows you to integrate  &lt;/span&gt;&lt;a style="font-family: Courier New;" title="Canoo WebTest" href="http://webtest.canoo.com/webtest/manual/WebTestHome.html" id="sinf"&gt;Canoo WebTest&lt;/a&gt;&lt;span style="font-family:Arial;"&gt; into your Grails project and create tests for automatic testing.&lt;/span&gt; &lt;span style="font-family:Arial;"&gt;In this post we take a look at possibility to run your tests with simulation of different locales and browser types. You can use described approach for other WebTest configuration parameters.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:Arial;"&gt;&lt;span style="font-family:Arial;"&gt;For examples I have used grails-1.1.1, Webtest plugin 1.1.4.2 and WebTest 3.0.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;h2 style="font-family: Arial;"&gt;Create test suite&lt;/h2&gt;&lt;span style="font-family:Arial;"&gt;Make sure the WebTest plugin is installed and available in your project, create some test:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre  style="font-family:Courier New;"&gt;grails install-plugin webtest&lt;br /&gt;grails create-webtest &lt;name&gt;&lt;br /&gt;&lt;br /&gt;&lt;/name&gt;&lt;/pre&gt;&lt;div style="text-align: justify;"&gt;&lt;name&gt;&lt;span style="font-family:Arial;"&gt;These commands will install WebTest plugin, make WebTest directory within your project, create &lt;span style="font-family:Courier New;"&gt;TestSuite&lt;/span&gt; class and Test class for your domain class.&lt;/span&gt;&lt;/name&gt;&lt;br /&gt;&lt;name&gt;&lt;span style="font-family:Arial;"&gt;In example project (download below) my domain class is called Car, so calling &lt;span style="font-family:Courier New;"&gt;grails create-webtest Car&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:Verdana;"&gt;&lt;span style="font-family:Arial;"&gt; creates &lt;span style="font-family:Courier New;"&gt;TestSuite.groovy&lt;/span&gt; and &lt;span style="font-family:Courier New;"&gt;CarTest.groovy&lt;/span&gt; files in &lt;span style="font-family:Courier New;"&gt;webtest/tests &lt;/span&gt;directory. The test class already contains generated tests for your domain class. You can use following command to run them:&lt;/span&gt;&lt;/span&gt;&lt;/name&gt;&lt;br /&gt;&lt;name&gt;&lt;/name&gt;&lt;/div&gt;&lt;pre face="Courier New"&gt;grails run-webtest&lt;name&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=";font-family:Arial;font-size:100%;"  &gt;When inspecting test results, you can see tests are run with your default language locale.&lt;/span&gt;&lt;br /&gt;&lt;/name&gt;&lt;/pre&gt;&lt;h2 style="font-family: Arial;"&gt;Implement static method that runs before each test.&lt;/h2&gt;&lt;div style="text-align: left;"&gt;&lt;span style="font-family:Arial;"&gt;The browser type or language locale in WebTest is set in &lt;/span&gt;&lt;a style="font-family: Arial;" title="config" href="http://webtest.canoo.com/webtest/manual/config.html" id="tghi"&gt;config&lt;/a&gt;&lt;span style="font-family:Arial;"&gt; (configuration option &lt;span style="font-family:Courier New;"&gt;browser&lt;/span&gt;) and &lt;/span&gt;&lt;a style="font-family: Arial;" title="header" href="http://webtest.canoo.com/webtest/manual/header.html" id="vcy:"&gt;header&lt;/a&gt;&lt;span style="font-family:Arial;"&gt; (parameter &lt;/span&gt;&lt;span class="example"  style="font-family:Arial;"&gt;&lt;span style="font-family:Courier New;"&gt;Accept-Language&lt;/span&gt;) &lt;/span&gt;&lt;span style="font-family:Arial;"&gt;elements.&lt;/span&gt; &lt;span style="font-family:Arial;"&gt;Before each test is run, we will explicitly set  these parameters to make sure the test is run with appropriate locale and browser emulation. &lt;/span&gt;&lt;span style="font-family:Arial;"&gt;For this we need a way to call  a (static) method before each test is run.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;Let us put this static method into TestSuite class and call it &lt;span style="font-family:Courier New;"&gt;globalSetUp&lt;/span&gt;:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;public static lang = 'en'&lt;br /&gt;public static browser = 'FF3'&lt;br /&gt;public static Properties i18nProperties = new Properties();&lt;br /&gt;&lt;br /&gt;def static globalSetUp(def ant) {&lt;br /&gt;ant.config(summary: true, browser: browser) {&lt;br /&gt;    header name: 'Accept-Language', value: lang&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;You can see the &lt;span style="font-family:Courier New;"&gt;globalSetUp&lt;/span&gt; method takes WebTest ant builder as a parameter and prepares config and header elements before test is created and run.&lt;/span&gt; &lt;span style="font-family:Arial;"&gt;It sets browser type and locale according to the values in &lt;span style="font-family:Courier New;"&gt;lang&lt;/span&gt; and &lt;span style="font-family:Courier New;"&gt;browser&lt;/span&gt; static variables.&lt;/span&gt;&lt;span style="font-family:Arial;"&gt; The static variable &lt;span style="font-family:Courier New;"&gt;i18nProperties&lt;/span&gt; will be discussed later.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;Each test class inherits &lt;span style="font-family:Courier New;"&gt;WebTest&lt;/span&gt; class. In case &lt;span style="font-family:Courier New;"&gt;setUp&lt;/span&gt; method exists in the test class, it is called before any test method in test class is run (see &lt;span style="font-family:Courier New;"&gt;WebTest.webtestMethod&lt;/span&gt; in &lt;span style="font-family:Courier New;"&gt;WebTest.groovy&lt;/span&gt;).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;We have two options to call our static method before any test from test class is invoked:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul  style="font-family:Courier New;"&gt;&lt;li  style="font-family:Arial;"&gt;add &lt;span style="font-family:Courier New;"&gt;setUp&lt;/span&gt; method to each test class, which in turns calls our static method&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:Arial;"&gt;make new class derived from &lt;span style="font-family:Courier New;"&gt;WebTest&lt;/span&gt; class and implement &lt;span style="font-family:Courier New;"&gt;setUp&lt;/span&gt; method in this class calling &lt;/span&gt;&lt;span style="font-family:Courier New;"&gt;globalSetUp&lt;/span&gt;&lt;span style="font-family:Arial;"&gt;; change each test class in a way that it is inherited from this new class instead of &lt;span style="font-family:Courier New;"&gt;WebTest&lt;/span&gt; class&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-family:Courier New;"&gt;  &lt;/span&gt;&lt;br /&gt;&lt;i style="font-family: Arial;"&gt;(Note:In one project I have encountered path problems with second option - new class couldn not be found - but in other project there was no problem; do not know why yet.)&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;Let's choose first option and add to each test class following &lt;span style="font-family:Courier New;"&gt;setUp&lt;/span&gt; method:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;def setUp() {&lt;br /&gt;TestSuite.globalSetUp(ant)&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h2 style="font-family: Arial;"&gt;Run suite with different browser emulation&lt;br /&gt;&lt;/h2&gt;&lt;span style="font-family:Arial;"&gt; As described in &lt;/span&gt;&lt;a style="font-family: Arial;" title="config" href="http://webtest.canoo.com/webtest/manual/config.html" id="tghi"&gt;config&lt;/a&gt;&lt;span style="font-family:Arial;"&gt; WebTest (version 3.0) can emulate following browsers:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul  style="font-family:Arial;"&gt;&lt;li&gt;   &lt;span class="example"&gt;Firefox3&lt;/span&gt; (&lt;span class="example"&gt;FF3&lt;/span&gt;)&lt;/li&gt;&lt;li&gt;&lt;span class="example"&gt;Firefox2&lt;/span&gt; (&lt;span class="example"&gt;FF2&lt;/span&gt;)&lt;/li&gt;&lt;li&gt;&lt;span class="example"&gt;InternetExplorer6&lt;/span&gt; (&lt;span class="example"&gt;IE6&lt;/span&gt;)&lt;/li&gt;&lt;li&gt;&lt;span class="example"&gt;InternetExplorer7&lt;/span&gt; (&lt;span class="example"&gt;IE7&lt;/span&gt;) &lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;To set browser emulation, change value of static variable &lt;span style="font-family:Courier New;"&gt;browser&lt;/span&gt;:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;e.g.:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Courier New;"&gt;&lt;br /&gt;public static browser = 'IE6'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Courier New;"&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;and run tests:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Courier New;"&gt;grails run-webtest&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;In output you should see text:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Courier New;"&gt;Surfing with browser IE6&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;h2 style="font-family: Arial;"&gt;Run suite with different locales.&lt;/h2&gt;&lt;span style="font-family:Arial;"&gt;To run TestSuite with different locale, change value of &lt;span style="font-family:Courier New;"&gt;lang&lt;/span&gt; static variable.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;E.g.:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Courier New;"&gt;public static lang = 'de'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;and run tests:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;grails run-webtest&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;In output you should see text:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Courier New;"&gt;&lt;br /&gt;Configured header "Accept-Language": de&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;The web pages in your text should reflect selected locale.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3 style="font-family: Arial;"&gt;verifyText for given locale&lt;/h3&gt;&lt;span style="font-family:Arial;"&gt;&lt;span style="font-family:Courier New;"&gt;verifyText&lt;/span&gt;  command makes sure there is a given text on the web page. In different locale the text will be different and we want to make sure our test suite takes this into consideration.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;The text in browser is (usually) rendered with g:message tag to resolve text from &lt;span style="font-family:Courier New;"&gt;message_&lt;locale&gt;.properties&lt;/locale&gt;&lt;/span&gt; file. &lt;/span&gt;&lt;span style="font-family:Arial;"&gt;In example project the the Car list view has header "CAR LIST" in English version and "AUTO-LISTE" in German version. This is achieved by line  &lt;span style="font-family:Courier New;"&gt;&lt;/span&gt; in &lt;span style="font-family:Courier New;"&gt;views/car/list.gsp&lt;/span&gt; file. This line gets the text from corresponding i18n properties file.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;If we want to test this text, we can add following line to &lt;span style="font-family:Courier New;"&gt;CarTest.groovy&lt;/span&gt;:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Courier New;"&gt;verifyText 'CAR LIST'&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;This line passes with English locale, but fails with German locale. &lt;/span&gt;&lt;span style="font-family:Arial;"&gt;To fix it, we will read message properties for given language into &lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:Arial;"&gt;&lt;span style="font-family:Courier New;"&gt;i18nProperties&lt;/span&gt; static variable (introduced earlier) before the tests are run.&lt;br /&gt;&lt;br /&gt;In &lt;span style="font-family:Courier New;"&gt;TestSuite.groovy&lt;/span&gt; file find&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;for (file in scanner) {&lt;br /&gt;def test = getClass().classLoader.parseClass(file).newInstance()&lt;br /&gt;test.ant = ant&lt;br /&gt;test.suite()&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span style="font-family:Courier New;"&gt;&lt;/span&gt;&lt;pre&gt;&lt;span style="font-family:Arial;"&gt;and change it to:&lt;/span&gt;&lt;/pre&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;TestSuite.i18nProperties.load(new FileInputStream("grails-app/i18n/messages_${lang}.properties"));&lt;br /&gt;for (file in scanner) {&lt;br /&gt;def test = getClass().classLoader.parseClass(file).newInstance()&lt;br /&gt;test.ant = ant&lt;br /&gt;test.suite()&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-family:Arial;"&gt;Modify previous &lt;span style="font-family:Courier New;"&gt;verifyText&lt;/span&gt; (so it uses text from properties file):&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Courier New;"&gt;verifyText TestSuite.i18nProperties.getProperty("webtest-multi.carlist")&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;Now, the test will pass for both, English and German locale.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;h2 style="font-family: Arial;"&gt;Multiple locales and browser emulations in one test suite&lt;br /&gt;&lt;/h2&gt;&lt;div style="text-align: justify;"&gt;&lt;span style="font-family:Arial;"&gt;If you want to change locale or browser type, you have to change static variable &lt;span style="font-family:Courier New;"&gt;lang&lt;/span&gt; or &lt;span style="font-family:Courier New;"&gt;browser&lt;/span&gt; before each run. Another option is to modify test suite method to iterate through browsers and/or locales and run test suite several times.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre  style="font-family:Verdana;"&gt;&lt;span style="font-family:Arial;"&gt;e.g.:&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;TestSuite.i18nProperties.load(new FileInputStream("grails-app/i18n/messages_${lang}.properties"));&lt;br /&gt;['IE6', 'IE7', 'FF2', 'FF3'].each { br -&amp;gt;&lt;br /&gt;browser = br&lt;br /&gt;for (file in scanner) {&lt;br /&gt;          def test = getClass().classLoader.parseClass(file).newInstance()&lt;br /&gt;          test.ant = ant&lt;br /&gt;          test.suite()&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;or:&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;['de', 'en'].each { lo -&amp;gt;&lt;br /&gt;  lang = lo&lt;br /&gt;  TestSuite.i18nProperties.load(new FileInputStream("grails-app/i18n/messages_${lang}.properties"));&lt;br /&gt; for (file in scanner) {&lt;br /&gt;           def test = getClass().classLoader.parseClass(file).newInstance()&lt;br /&gt;           test.ant = ant&lt;br /&gt;           test.suite()&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;or even:&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;['de', 'en'].each { lo -&amp;gt;&lt;br /&gt;  lang = lo&lt;br /&gt;  TestSuite.i18nProperties.load(new FileInputStream("grails-app/i18n/messages_${lang}.properties"));&lt;br /&gt;  ['IE6', 'IE7', 'FF2', 'FF3'].each { br -&amp;gt;&lt;br /&gt;          browser = br&lt;br /&gt;          for (file in scanner) {&lt;br /&gt;                       def test = getClass().classLoader.parseClass(file).newInstance()&lt;br /&gt;                       test.ant = ant&lt;br /&gt;                       test.suite()&lt;br /&gt;          }&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;&lt;span style="font-family:Arial;"&gt;At first glance, this approach seems obvious. Unfortunately it is only useful for test suites which do not change the context (database) after they pass or fail or for test suites that do not depend on this context (for example no dependency on number of rows &lt;/span&gt;&lt;span style="font-family:Arial;"&gt;in the table). If you do not have such tests, you have to reset the context after each run of suite (after call of &lt;span style="font-family:Courier New;"&gt;test.suite()&lt;/span&gt; method) in some way. Currently I do not know any proper way how to do it.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre face="Verdana"&gt;&lt;br /&gt;&lt;/pre&gt;&lt;h2 style="font-family: Arial;"&gt;Conclusion&lt;/h2&gt;&lt;span style="font-family:Arial;"&gt;WebTest is testing framework which is well integrated into grails. In this post you have seen how to modify attributes of &lt;span style="font-family:Courier New;"&gt;config&lt;/span&gt; and &lt;span style="font-family:Courier New;"&gt;header&lt;/span&gt; elements for your tests and how to use this modification to emulate different browser types and change language locales. This is can be even more useful, if you can iterate trough the array of possible values directly in &lt;span style="font-family:Courier New;"&gt;TestSuite&lt;/span&gt; script. Unfortunately this is only possible for tests that do not change database context.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2 style="font-family: Arial;"&gt;External links:&lt;br /&gt;&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a title="example grails project" href="http://bubbles-way-utils.googlecode.com/files/webtest-multi_20090525.zip" id="d-gu"&gt;example grails project&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a title="WebTest plugin" href="http://grails.org/plugin/webtest" id="apxl"&gt;WebTest plugin&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a title="WebTest" href="http://webtest.canoo.com/webtest/manual/WebTestHome.html" id="lh5p"&gt;Canoo WebTest&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4151123446707233521-6567557616424679371?l=it-bubbles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://it-bubbles.blogspot.com/feeds/6567557616424679371/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4151123446707233521&amp;postID=6567557616424679371' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4151123446707233521/posts/default/6567557616424679371'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4151123446707233521/posts/default/6567557616424679371'/><link rel='alternate' type='text/html' href='http://it-bubbles.blogspot.com/2009/05/webtest-grails.html' title='Test different language locale and browser type with Grails WebTest plugin.'/><author><name>Bubbles</name><uri>http://www.blogger.com/profile/06938554422574447792</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4151123446707233521.post-3325280403556875146</id><published>2009-03-27T08:31:00.001-07:00</published><updated>2009-05-26T14:30:32.221-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='calendar'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='grails'/><title type='text'>Enhance Grails g:datePicker with jscalendar</title><content type='html'>&lt;span style="font-size:100%;"&gt;&lt;span style="font-weight: bold;font-size:180%;" &gt;Introduction&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;(Note 2009-05-17: Calendar plugin 1.2.0-SNAPSHOT broke interface of jscalendar by adding custom code to calendar.js file. You either have to use earlier calendar  plugin, replace calendar.js file with original or change the implementation to adapt to Calendar plugin 1.2.0., if possible. Another option is to use original jscalendar without support of calendar plugin at all. Personally I do not like the original - non plugin - calendar.js is modified by plugin developer. Plugin should be wrapper around calendar.js.)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Grails has &lt;a href="http://grails.org/doc/1.1/"&gt;&lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt;&lt;/a&gt; gsp tag to create editable date and time field. &lt;/span&gt;&lt;span style="font-size:100%;"&gt;With &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt; you can choose current date and time through combo boxes (selects).&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_XhWSZRa4fvE/Sczym_9e0bI/AAAAAAAAAAU/3ANZkJdvBPg/s1600-h/grails-datepicker.jpg"&gt;&lt;img style="cursor: pointer; width: 320px; height: 31px;" src="http://4.bp.blogspot.com/_XhWSZRa4fvE/Sczym_9e0bI/AAAAAAAAAAU/3ANZkJdvBPg/s320/grails-datepicker.jpg" alt="" id="BLOGGER_PHOTO_ID_5317892011835969970" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;There is also &lt;a href="http://grails.org/plugin/calendar"&gt;calendar plugin&lt;/a&gt; which brings &lt;a href="http://www.dynarch.com/projects/calendar/"&gt; jscalendar&lt;/a&gt; into grails. &lt;span style="font-size:100%;"&gt;You can open jscalendar (e.g. clicking on calendar picture) and choose date and time. It is not possible to edit date in associated text field (there is&lt;a href="http://jira.codehaus.org/browse/GRAILSPLUGINS-890"&gt; &lt;/a&gt;&lt;a style="border-bottom: 1px solid orange;" class="autolink autolink-ticker-symbol" title="Link added by Stock Chart AutoLink filter: Ticker Symbol" href="http://jira.codehaus.org/browse/GRAILSPLUGINS-890"&gt;JIRA&lt;/a&gt; issue for this).&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_XhWSZRa4fvE/SczzM2zRKeI/AAAAAAAAAAc/NCJKrP4AwGc/s1600-h/grails-caledar-datepicker.jpg"&gt;&lt;img style="cursor: pointer; width: 179px; height: 32px;" src="http://3.bp.blogspot.com/_XhWSZRa4fvE/SczzM2zRKeI/AAAAAAAAAAc/NCJKrP4AwGc/s320/grails-caledar-datepicker.jpg" alt="" id="BLOGGER_PHOTO_ID_5317892662212241890" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;Very often, you would like to have both functionality combined. I have created javascript function that links &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt; with jscalendar via onclick action.&lt;br /&gt;&lt;br /&gt;The result looks like this (by clicking on image, cale&lt;/span&gt;&lt;span style="font-size:100%;"&gt;ndar pop-ups):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_XhWSZRa4fvE/Scz1cLWHJmI/AAAAAAAAAAk/0jLYLOVdcmE/s1600-h/grails-datepicker-with-calendar.jpg"&gt;&lt;img style="cursor: pointer; width: 320px; height: 27px;" src="http://2.bp.blogspot.com/_XhWSZRa4fvE/Scz1cLWHJmI/AAAAAAAAAAk/0jLYLOVdcmE/s320/grails-datepicker-with-calendar.jpg" alt="" id="BLOGGER_PHOTO_ID_5317895124448388706" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I have tried to meet following requirements:&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;ul&gt;&lt;li&gt;reuse &lt;span style="font-family:courier new;"&gt;g:datePicke&lt;/span&gt;r configuration in associated jscalendar as much as possible (no need to repeat configuration parameters)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;automatic synchronization between value in &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt; and jscalendar&lt;/li&gt;&lt;li&gt;support for internationalization - add possibility to use time format from calendar lang file, use first day of week from calendar lang file&lt;br /&gt;&lt;/li&gt;&lt;li&gt;simple to use&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;When user clicks on calendar image/button associated with&lt;span style="font-family:courier new;"&gt; g:datePicker&lt;/span&gt;, jscalendar widget is created and displayed. It already has date (and time in case  &lt;span style="font-family:courier new;"&gt;g:datePicker precision&lt;/span&gt; parameter is hour or minute) filled with date corresponding to values in &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt; combo boxes. By changing the date (and time) in jscalendar the  &lt;span style="font-family:courier new;"&gt;g:datePicker &lt;/span&gt;values change as well. Single click on the date closes calendar.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_XhWSZRa4fvE/Scz_kQsHMBI/AAAAAAAAAAs/W7nauuumb7g/s1600-h/grails-datepicker-with-calendar-displayed.jpg"&gt;&lt;img style="cursor: pointer; width: 320px; height: 194px;" src="http://3.bp.blogspot.com/_XhWSZRa4fvE/Scz_kQsHMBI/AAAAAAAAAAs/W7nauuumb7g/s320/grails-datepicker-with-calendar-displayed.jpg" alt="" id="BLOGGER_PHOTO_ID_5317906258438074386" border="0" /&gt;&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;If javascript is disabled (e.g. with NoScript Firefox add on), user can still select date and time through &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:180%;" &gt;Implementation&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Javascript function is called  &lt;span style="font-family:courier new;"&gt;showCalendar&lt;/span&gt; and it  links jscalendar&lt;/span&gt;&lt;span style="font-size:100%;"&gt; with &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt;.&lt;br /&gt;&lt;span style="font-family:georgia;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-family:georgia;"&gt;g:datePicker&lt;/span&gt;&lt;span style="font-family:courier new;"&gt; &lt;/span&gt;accepts following arguments (see documentation):&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-family:courier new;"&gt;name, value, precision, years &lt;span style="font-family:arial;"&gt;and&lt;/span&gt; noSelection&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;When examining the source code for &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt;, you can see it creates several combo box elements with following ids:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;${name}_year&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;${name}_month&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;${name}_date&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;${name}_hour&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;${name}_minute&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;br /&gt;According to &lt;span style="font-family:courier new;"&gt;precision&lt;/span&gt; parameter, some combo boxes may not be created . Year combo box is always created.&lt;br /&gt;&lt;br /&gt;In gsp page you  link &lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-family:courier new;"&gt;showCalendar&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt; function with  &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt; in following way:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;&amp;lt;g:datePicker name="reminderAt" value="${eventInstance?.reminderAt}" &amp;gt;&amp;lt;/g:datePicker&amp;gt;&lt;br /&gt;&amp;lt;img src="${createLinkTo(&lt;br /&gt;            dir: org.codehaus.groovy.grails.plugins.PluginManagerHolder.currentPluginManager().getGrailsPlugin("calendar").getPluginPath(),&lt;br /&gt;            file: "/images/skin/calendar.png")}"&lt;br /&gt;id="reminderAt-trigger" alt="Date"&lt;br /&gt;onclick="showCalendar('reminderAt');"/&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-style: italic;"&gt;(Note: In this example I have tried to re-use calendar image directly from the calendar plugin. That is why the dir path is so cumbersome. If you know of better way how to get plugin's dir, please let me know. You can use path to any other image or use button instead.)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The parameters of &lt;span style="font-family:courier new;"&gt;showCalendar&lt;/span&gt; function are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;name of &lt;span style="font-family:courier new;"&gt;g:datePicker &lt;span style="font-family:arial;"&gt;to link function with&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;boolean value indicating if time is displayed in 24h format - this parameter is optional and can be omitted (default is true = 24 hour format - this is in line with &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt; format). Preferred way to set time format should be through &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:courier new;"&gt;calendar-{lang}.js &lt;span style="font-family:arial;"&gt;(see below).&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;From &lt;span style="font-family:courier new;"&gt;calendar-{lang}.js&lt;/span&gt; following parameters are used:&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;Calendar._FD - first day of week (if not specified, default value is 0 = Sunday)&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;Calendar._TIME24&lt;/span&gt; - if true, 24h time format is used (if specified it overwrites second parameter of  &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:courier new;"&gt;showCalendar &lt;span style="font-family:arial;"&gt;function). This parameter is newly introduced by my implementation (as I thing this is lang specific like &lt;span style="font-family:courier new;"&gt;Calendar._FD&lt;/span&gt;) and you have to modify &lt;span style="font-family:courier new;"&gt;calendar-{lang).js&lt;/span&gt; if you want to use it. Otherwise pass time format as second parameter when calling &lt;span style="font-family:courier new;"&gt;showCalendar&lt;/span&gt;.&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Calendar creation&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Upon creation of jscalendar the fu&lt;/span&gt;&lt;span style="font-size:100%;"&gt;nction tries to find out most of the parameters from associated &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Following jscalendar parameters are configured at that time:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;date corresponding to the date in &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt; or today's date if year, month or day in the  &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt;&lt;/span&gt; corresponds to &lt;span style="font-family:courier new;"&gt;noSelection &lt;/span&gt;value&lt;/li&gt;&lt;li&gt;allowed year range corresponding to possible values in &lt;span style="font-family:courier new;"&gt;${name}_year&lt;/span&gt; combo box element&lt;br /&gt;&lt;/li&gt;&lt;li&gt;presence of time setup - time setup is part of jscalendar only if &lt;span style="font-family:courier new;"&gt;${name}_hour&lt;/span&gt; element exists&lt;/li&gt;&lt;li&gt;time format is set to 24h format by default; it can be changed to 12h format by passing false as second (optional) parameter to the function; if time format is specified in &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:courier new;"&gt;calendar-{lang}.js (&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;Calendar._TIME24&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-family:courier new;"&gt;)&lt;/span&gt;&lt;/span&gt;, it is used instead, with higher priority.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Calendar is displayed next to &lt;span style="font-family:courier new;"&gt;${name}_year&lt;/span&gt; combo box element (this element is always present regardless of &lt;span style="font-family:courier new;"&gt;precision&lt;/span&gt; attribute).&lt;br /&gt;&lt;br /&gt;Note: Since jscalednar is linked to &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt;, there is no need to configure date format in any way, which makes configuration easy.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Selection - event handling:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;When date is changed in&lt;/span&gt;&lt;span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-size:100%;"&gt; jscalendar, event function &lt;span style="font-family:courier new;"&gt;selected&lt;/span&gt; is called. It extracts selected Date and u&lt;/span&gt;&lt;span style="font-size:100%;"&gt;pdates &lt;span style="font-family:courier new;"&gt;g:datePicker &lt;/span&gt;combo boxes accordingly.&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Source code:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;/*&lt;br /&gt;File: calendar-gdatepicker.js&lt;br /&gt;Grails g:datePicker binding to the&lt;br /&gt;&lt;a style="border-bottom: 1px solid orange;" class="autolink autolink-ticker-symbol" title="Link added by Stock Chart AutoLink filter: Ticker Symbol" href="http://stockcharts.com/def/servlet/SC.web?c=DHTML"&gt;DHTML&lt;/a&gt; Calendar www.dynarch.com/projects/calendar&lt;br /&gt;version: 1.1&lt;br /&gt;history:&lt;br /&gt;1.1  2009-03-29 firstDayOfWeek is retrieved from calendar-{lang}.js file Calendar._FD&lt;br /&gt; time24 format is retrieved from calendar-{lang}.js file Calendar._TIME24&lt;br /&gt; (Michal Novak)&lt;br /&gt;1.0  2009-03-28 Initial version (Michal Novak)&lt;br /&gt;&lt;br /&gt;Copyright 2009, Michal Novak, bubbles.way@gmail.com&lt;br /&gt;&lt;a style="border-bottom: 1px solid orange;" class="autolink autolink-plain-text-link" title="Link added by Stock Chart AutoLink filter: Plain text link" href="http://it-bubbles.blogspot.com/"&gt;http://it-bubbles.blogspot.com/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This script is distributed under the &lt;a style="border-bottom: 1px solid orange;" class="autolink autolink-ticker-symbol" title="Link added by Stock Chart AutoLink filter: Ticker Symbol" href="http://stockcharts.com/def/servlet/SC.web?c=GNU"&gt;GNU&lt;/a&gt; Lesser General Public License.&lt;br /&gt;Read the entire license text here: &lt;a style="border-bottom: 1px solid orange;" class="autolink autolink-plain-text-link" title="Link added by Stock Chart AutoLink filter: Plain text link" href="http://www.gnu.org/licenses/lgpl.html"&gt;http://www.gnu.org/licenses/lgpl.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This script is base on examples on following page:&lt;br /&gt;&lt;a style="border-bottom: 1px solid orange;" class="autolink autolink-plain-text-link" title="Link added by Stock Chart AutoLink filter: Plain text link" href="http://www.dynarch.com/static/jscalendar-1.0/index.html"&gt;http://www.dynarch.com/static/jscalendar-1.0/index.html&lt;/a&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;// return value given by index of combo box element or null if combo box element is null&lt;br /&gt;function comboGetValueAt(comboBoxElem, index) {&lt;br /&gt;if (comboBoxElem != null) {&lt;br /&gt;if (comboBoxElem.options.length &amp;gt; index) {&lt;br /&gt;        return comboBoxElem.options[index].value;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;return null;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// return selected value of combo box element  or null if combo box element is null&lt;br /&gt;function comboSelectedValue(comboBoxElem) {&lt;br /&gt;if (comboBoxElem != null) {&lt;br /&gt;return comboGetValueAt(comboBoxElem, comboBoxElem.selectedIndex)&lt;br /&gt;}&lt;br /&gt;return null;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// select value in combo box element (if combo box element is not null)&lt;br /&gt;function selectCombo(comboBoxElem, value) {&lt;br /&gt;if (comboBoxElem != null) {&lt;br /&gt;for (var i = 0; i &amp;lt; comboBoxElem.options.length; i++) {&lt;br /&gt;        if (comboBoxElem.options[i].value == value &amp;amp;&amp;amp;&lt;br /&gt;                comboBoxElem.options[i].value != "") { //empty string is for "noSelection handling as "" == 0 in js&lt;br /&gt;                comboBoxElem.options[i].selected = true;&lt;br /&gt;                break&lt;br /&gt;        }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// This function gets called when the end-user clicks on some date.&lt;br /&gt;function selected(cal) {&lt;br /&gt;selectCombo(cal.g_year, cal.date.getFullYear())&lt;br /&gt;selectCombo(cal.g_month, cal.date.getMonth() + 1)&lt;br /&gt;selectCombo(cal.g_day, cal.date.getDate())&lt;br /&gt;selectCombo(cal.g_hour, cal.date.getHours())&lt;br /&gt;selectCombo(cal.g_minute, cal.date.getMinutes())&lt;br /&gt;if (cal.dateClicked)&lt;br /&gt;//close the calendar on single-click.&lt;br /&gt;cal.callCloseHandler();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// This function gets called when the end-user clicks on the _selected_ date,&lt;br /&gt;// or clicks on the "Close" button. It just hides the calendar without destroying it.&lt;br /&gt;function closeHandler(cal) {&lt;br /&gt;cal.hide()                       // hide the calendar&lt;br /&gt;_dynarch_popupCalendar = null&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// This function shows the calendar under the year element of the g:datePicker&lt;br /&gt;// first parameter is id (name) of g:datePicker element&lt;br /&gt;// second parameter (optional) specifies if time should be displayed in 24h format (default is true)&lt;br /&gt;// other necessary configuration is extracted directly from g:datePicker&lt;br /&gt;function showCalendar(datePickerId,time24) {&lt;br /&gt;if (_dynarch_popupCalendar != null) { // we already have some calendar created&lt;br /&gt;_dynarch_popupCalendar.hide() // so we hide it first.&lt;br /&gt;}&lt;br /&gt;// create the calendar&lt;br /&gt;var firstDayOfWeek = Calendar._FD == null?0:Calendar._&lt;a style="border-bottom: 1px solid orange;" class="autolink autolink-ticker-symbol" title="Link added by Stock Chart AutoLink filter: Ticker Symbol" href="http://stockcharts.com/def/servlet/SC.web?c=FS"&gt;FS&lt;/a&gt; // default is 0 (Sun) if not specified in lang file&lt;br /&gt;time24 = time24 == null?true:false // default time format is true (24h)&lt;br /&gt;time24 = Calendar._TIME24 == null?time24:Calendar._TIME24 // time format can be overwritten from lang file&lt;br /&gt;_dynarch_popupCalendar = new Calendar(firstDayOfWeek, null, selected, closeHandler)&lt;br /&gt;// remember grails date picker elements&lt;br /&gt;_dynarch_popupCalendar.g_year = document.getElementById(datePickerId + '_year')&lt;br /&gt;_dynarch_popupCalendar.g_month = document.getElementById(datePickerId + '_month')&lt;br /&gt;_dynarch_popupCalendar.g_day = document.getElementById(datePickerId + '_day')&lt;br /&gt;_dynarch_popupCalendar.g_hour = document.getElementById(datePickerId + '_hour')&lt;br /&gt;_dynarch_popupCalendar.g_minute = document.getElementById(datePickerId + '_minute')&lt;br /&gt;_dynarch_popupCalendar.showsTime = _dynarch_popupCalendar.g_hour != null // display time only if grails has hour element&lt;br /&gt;_dynarch_popupCalendar.time24 = time24&lt;br /&gt;// find out year range from grails date picker&lt;br /&gt;var minYear = comboGetValueAt(_dynarch_popupCalendar.g_year, 0) // get first item&lt;br /&gt;var maxYear = comboGetValueAt(_dynarch_popupCalendar.g_year, _dynarch_popupCalendar.g_year.options.length - 1) // get first item&lt;br /&gt;_dynarch_popupCalendar.setRange(parseInt(minYear), parseInt(maxYear)) // min/max year allowed.&lt;br /&gt;_dynarch_popupCalendar.create()&lt;br /&gt;var yearValue = comboSelectedValue(_dynarch_popupCalendar.g_year)&lt;br /&gt;var monthValue = comboSelectedValue(_dynarch_popupCalendar.g_month)&lt;br /&gt;var dayValue = comboSelectedValue(_dynarch_popupCalendar.g_day)&lt;br /&gt;var hourValue = comboSelectedValue(_dynarch_popupCalendar.g_hour)&lt;br /&gt;var minuteValue = comboSelectedValue(_dynarch_popupCalendar.g_minute)&lt;br /&gt;// day and month may not be present in grails date picker and if set to null, it makes calendar to break&lt;br /&gt;monthValue = monthValue == null || monthValue == "" ? 0 : parseInt(comboSelectedValue(_dynarch_popupCalendar.g_month)) - 1&lt;br /&gt;dayValue = dayValue == null ? 1 : comboSelectedValue(_dynarch_popupCalendar.g_day)&lt;br /&gt;var ourDate = new Date(yearValue, monthValue, dayValue, hourValue, minuteValue)&lt;br /&gt;// handle grails date picker "noSelection: in year, month or day combo box, in this case set value to today&lt;br /&gt;if (ourDate.getDate() != dayValue || ourDate.getMonth() != monthValue || ourDate.getFullYear() != yearValue) {&lt;br /&gt;ourDate = new Date()&lt;br /&gt;}&lt;br /&gt;_dynarch_popupCalendar.setDate(ourDate)&lt;br /&gt;_dynarch_popupCalendar.showAtElement(_dynarch_popupCalendar.g_year) // show the calendar&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/bubbles-way-utils/source/browse/tags/grails/calendar-gdatepicker/V1_1/calendar-gdatepicker.js"&gt;Download link&lt;/a&gt;&lt;br /&gt;&lt;span style="font-size:180%;"&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Integration with grails&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Integration with grails is  simple. Copy &lt;span style="font-family:courier new;"&gt;calendar-gdatepicker.js&lt;/span&gt; source file to &lt;span style="font-family:courier new;"&gt;web-app/js&lt;/span&gt; directory of your  grails project.&lt;br /&gt;Import jscalendar and  &lt;span style="font-family:courier new;"&gt;calendar-gdatepicker.js &lt;/span&gt;scripts on your page.&lt;br /&gt;&lt;br /&gt;Since Grails already have calendar plugin, I think the best way is to use this plugin to import jscalendar and jscalendar localization scripts. Then you can choose which localization and theme to use (this requires that calendar plugin is installed in grails).&lt;br /&gt;&lt;br /&gt;E.g:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;&amp;lt;calendar:resources lang="en" theme="system"/&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;(Of course another way is to import jscalendar scripts directly).&lt;br /&gt;&lt;br /&gt;Import &lt;span style="font-family:courier new;"&gt;calendar-gdatepicker.js&lt;/span&gt; script:&lt;br /&gt;&lt;pre face="Andale Mono,Lucida Console,Monaco,fixed,monospace" size="12px" style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); line-height: 14px; width: 100%;"&gt;&lt;code&gt;&amp;lt;script type="text/javascript" src="${createLinkTo(dir:'js',file:'calendar-gdatepicker.js')}"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Now, you are ready to use jscalendar linked with &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt;. You only need to declare element (usually  after &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt; tag) with onclick method set to &lt;span style="font-family:courier new;"&gt;showCalendar&lt;/span&gt; with first parameter specifying the name of &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt;. The most appropriate element is usually image or button.&lt;br /&gt;&lt;br /&gt;E.g:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;&amp;lt;g:datePicker name="reminderAt" value="${eventInstance?.reminderAt}" &amp;gt;&amp;lt;/g:datePicker&amp;gt;&lt;br /&gt;&amp;lt;img src="${createLinkTo(&lt;br /&gt;            dir: org.codehaus.groovy.grails.plugins.PluginManagerHolder.currentPluginManager().getGrailsPlugin("calendar").getPluginPath(),&lt;br /&gt;            file: "/images/skin/calendar.png")}"&lt;br /&gt;id="reminderAt-trigger" alt="Date"&lt;br /&gt;onclick="showCalendar('reminderAt');"/&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;In InternetExplorer the jscalendar plugin (ver. 1.1.1) provides styling than makes calendar to stretch. This should be fixed in 1.2.0 according to this  &lt;a href="http://jira.codehaus.org/browse/GRAILSPLUGINS-979"&gt;issue&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:180%;" &gt;Sample demo project &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I have created demo project that displays calendar from the plugin, calendar linked to the &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt; with full &lt;span style="font-family:courier new;"&gt;precision&lt;/span&gt; and calendar linked to the &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt; with day &lt;span style="font-family:courier new;"&gt;precision&lt;/span&gt;. &lt;/span&gt;Look for&lt;span style="font-family:courier new;"&gt; calendar-gdatepicker.js&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;create.gsp&lt;/span&gt; files in the project for example code.&lt;br /&gt;If you change&lt;span style="font-family:courier new;"&gt; lang&lt;/span&gt; parameter from &lt;span style="font-family:courier new;"&gt;"en"&lt;/span&gt; to&lt;span style="font-family:courier new;"&gt; "cs"&lt;/span&gt; (in &lt;span style="font-family:courier new;"&gt;create.gsp&lt;/span&gt;) you can see how calendar adjusts for Czech locale (&lt;span style="font-family:courier new;"&gt;calendar-cs.js&lt;/span&gt; is included).&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style="font-size:100%;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_XhWSZRa4fvE/Sc-ABKDDnRI/AAAAAAAAAA0/7W-CyqbEFaw/s1600-h/calendar-gdatepicker-demo.jpg"&gt;&lt;img style="cursor: pointer; width: 320px; height: 204px;" src="http://1.bp.blogspot.com/_XhWSZRa4fvE/Sc-ABKDDnRI/AAAAAAAAAA0/7W-CyqbEFaw/s320/calendar-gdatepicker-demo.jpg" alt="" id="BLOGGER_PHOTO_ID_5318610442313243922" border="0" /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:180%;" &gt;Conclusion&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;By extending &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt; with jscalendar support you can simplify date and time selection. Selection can be done directly in jscalendar, there is no need to go through all combo box elements of &lt;span style="font-family:courier new;"&gt;g:datePicker &lt;/span&gt;(on the other hand, if javascript is disabled, there is still a way to select date and time just through &lt;span style="font-family:courier new;"&gt;g:datePicker &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-size:100%;"&gt;without jscalendar)&lt;br /&gt;&lt;br /&gt;I believe this implementation (or similar) should become part of calendar plugin. New custom tag for linking &lt;span style="font-family:courier new;"&gt;g:datePicker&lt;/span&gt; with jscalendar would simplify usage in gsp files. This tag can have parameter specifying to show button or calendar image. Another benefit would be that there will be no need to import &lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-family:courier new;"&gt;calendar-gdatepicker.js&lt;/span&gt;&lt;/span&gt; as this could be handled by plugin's &lt;span style="font-family:courier new;"&gt;resource&lt;/span&gt; tag. I think such integration will resolve this &lt;a href="http://jira.codehaus.org/browse/GRAILSPLUGINS-1026"&gt;issue&lt;/a&gt; as well.&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;Feel free to use the script if you like it for any purpose. If you modify it, please keep original header information and add description of your modification to the header.&lt;br /&gt;&lt;br /&gt;This is my first javascript related work, so please be patient with possible errors or parts that does not conform to best practice rules.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:180%;"&gt;External links:&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.dynarch.com/projects/calendar/"&gt;jscalendar home page&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://grails.org/plugin/calendar"&gt;grails calendar plugin&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://grails.org/doc/1.1/"&gt;g:datePicker&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://bubbles-way-utils.googlecode.com/files/gdatepicker-demo_20090329.zip"&gt;example grails project&lt;/a&gt; (made with Netbeans 6.7 M2, Grails 1.0.4)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-size:100%;"&gt;&lt;img src="file:///C:/DOCUME%7E1/novak/LOCALS%7E1/Temp/moz-screenshot.jpg" alt="" /&gt;&lt;img src="file:///C:/DOCUME%7E1/novak/LOCALS%7E1/Temp/moz-screenshot-1.jpg" alt="" /&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4151123446707233521-3325280403556875146?l=it-bubbles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://it-bubbles.blogspot.com/feeds/3325280403556875146/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4151123446707233521&amp;postID=3325280403556875146' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4151123446707233521/posts/default/3325280403556875146'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4151123446707233521/posts/default/3325280403556875146'/><link rel='alternate' type='text/html' href='http://it-bubbles.blogspot.com/2009/03/enhace-grails-gdatepicker-with.html' title='Enhance Grails g:datePicker with jscalendar'/><author><name>Bubbles</name><uri>http://www.blogger.com/profile/06938554422574447792</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_XhWSZRa4fvE/Sczym_9e0bI/AAAAAAAAAAU/3ANZkJdvBPg/s72-c/grails-datepicker.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4151123446707233521.post-1459820905541256672</id><published>2009-02-12T14:15:00.001-08:00</published><updated>2009-02-12T14:28:54.863-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='stringtomap'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>StringToMap</title><content type='html'>First version of &lt;a href="http://code.google.com/p/stringtomap/"&gt;StringToMap&lt;/a&gt; has been just released.&lt;br /&gt;&lt;br /&gt;This small utility class/library can convert string with key:value items separated by separator character to Java Map. This is helpful when dealing with resource bundles for Swing/SWT UI, as you can put more values to one resource bundle property.&lt;br /&gt;&lt;br /&gt;E.g.:&lt;br /&gt;&lt;span style="font-family:monospace;"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;gui.action.file.opendb = lb:Open database...|mn:O|ic:disk.png|tp:Open existing database from specific directory.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Will result in following Map (keys and values are separated by ':'):&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:monospace;"&gt;&lt;br /&gt;lb:Open database...,&lt;br /&gt;mn:O|ic:disk.png,&lt;br /&gt;tp:Open existing database from specific directory.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Now it is easy to get value for label with&lt;span style="font-family:courier new;"&gt; get("lb")&lt;/span&gt;, mnemonic&lt;span style="font-family:courier new;"&gt;&lt;span style="font-family:arial;"&gt;&lt;/span&gt;&lt;/span&gt; &lt;span style="font-family:courier new;"&gt;get("mn")&lt;/span&gt; or tooltip &lt;span style="font-family:courier new;"&gt;get("tp")&lt;/span&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4151123446707233521-1459820905541256672?l=it-bubbles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://it-bubbles.blogspot.com/feeds/1459820905541256672/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4151123446707233521&amp;postID=1459820905541256672' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4151123446707233521/posts/default/1459820905541256672'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4151123446707233521/posts/default/1459820905541256672'/><link rel='alternate' type='text/html' href='http://it-bubbles.blogspot.com/2009/02/stringtomap.html' title='StringToMap'/><author><name>Bubbles</name><uri>http://www.blogger.com/profile/06938554422574447792</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4151123446707233521.post-7683143840988206503</id><published>2009-01-10T15:26:00.000-08:00</published><updated>2009-01-13T02:45:52.439-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bubbles'/><category scheme='http://www.blogger.com/atom/ns#' term='testng'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>TestNG dependency observation</title><content type='html'>&lt;p class="line874"&gt;I have observed behavior in TestNG dependency functionality that I do not fully understand.  &lt;span class="anchor" id="line-4"&gt;&lt;/span&gt;(In example below TestNG version 5.8 was used.) &lt;span class="anchor" id="line-5"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-6"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line874"&gt;Let us have following test class: &lt;span class="anchor" id="line-7"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-8"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-9"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-10"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-11"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-12"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-13"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-14"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-15"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-16"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-17"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-18"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-19"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-20"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-21"&gt;&lt;/span&gt;&lt;/p&gt;&lt;div class="codearea" dir="ltr" lang="en"&gt; &lt;script type="text/javascript"&gt; function isnumbered(obj) {   return obj.childNodes.length &amp;&amp; obj.firstChild.childNodes.length &amp;&amp; obj.firstChild.firstChild.className == 'LineNumber'; } function nformat(num,chrs,add) {   var nlen = Math.max(0,chrs-(''+num).length), res = '';   while (nlen&gt;0) { res += ' '; nlen-- }   return res+num+add; } function addnumber(did, nstart, nstep) {   var c = document.getElementById(did), l = c.firstChild, n = 1;   if (!isnumbered(c))     if (typeof nstart == 'undefined') nstart = 1;     if (typeof nstep  == 'undefined') nstep = 1;     n = nstart;     while (l != null) {       if (l.tagName == 'SPAN') {         var s = document.createElement('SPAN');         s.className = 'LineNumber'         s.appendChild(document.createTextNode(nformat(n,4,' ')));         n += nstep;         if (l.childNodes.length)           l.insertBefore(s, l.firstChild)         else           l.appendChild(s)       }       l = l.nextSibling;     }   return false; } function remnumber(did) {   var c = document.getElementById(did), l = c.firstChild;   if (isnumbered(c))     while (l != null) {       if (l.tagName == 'SPAN' &amp;&amp; l.firstChild.className == 'LineNumber') l.removeChild(l.firstChild);       l = l.nextSibling;     }   return false; } function togglenumber(did, nstart, nstep) {   var c = document.getElementById(did);   if (isnumbered(c)) {     remnumber(did);   } else {     addnumber(did,nstart,nstep);   }   return false; } &lt;/script&gt; &lt;pre dir="ltr" id="CA-ea8a3800f902f3ec8d2f42ea3991696b452c66d9" lang="en"&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   1 &lt;/span&gt;&lt;span class="ResWord"&gt;public&lt;/span&gt; &lt;span class="ResWord"&gt;class&lt;/span&gt; &lt;span class="ID"&gt;NGTest&lt;/span&gt; &lt;span class="SPChar"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   2 &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   3 &lt;/span&gt;        @&lt;span class="ID"&gt;Test&lt;/span&gt;&lt;span class="SPChar"&gt;(&lt;/span&gt;&lt;span class="ID"&gt;groups&lt;/span&gt; &lt;span class="SPChar"&gt;=&lt;/span&gt; &lt;span class="SPChar"&gt;{&lt;/span&gt;&lt;span class="String"&gt;"initA"&lt;/span&gt;&lt;span class="SPChar"&gt;}&lt;/span&gt;&lt;span class="SPChar"&gt;,&lt;/span&gt; &lt;span class="ID"&gt;enabled&lt;/span&gt; &lt;span class="SPChar"&gt;=&lt;/span&gt; &lt;span class="ConsWord"&gt;true&lt;/span&gt;&lt;span class="SPChar"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   4 &lt;/span&gt;        &lt;span class="ResWord"&gt;public&lt;/span&gt; &lt;span class="ResWord"&gt;void&lt;/span&gt; &lt;span class="ID"&gt;initA&lt;/span&gt;&lt;span class="SPChar"&gt;(&lt;/span&gt;&lt;span class="SPChar"&gt;)&lt;/span&gt; &lt;span class="SPChar"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   5 &lt;/span&gt;                &lt;span class="ID"&gt;System&lt;/span&gt;&lt;span class="SPChar"&gt;.&lt;/span&gt;&lt;span class="ID"&gt;out&lt;/span&gt;&lt;span class="SPChar"&gt;.&lt;/span&gt;&lt;span class="ID"&gt;println&lt;/span&gt;&lt;span class="SPChar"&gt;(&lt;/span&gt;&lt;span class="String"&gt;"Init A called."&lt;/span&gt;&lt;span class="SPChar"&gt;)&lt;/span&gt;&lt;span class="SPChar"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   6 &lt;/span&gt;        &lt;span class="SPChar"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   7 &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   8 &lt;/span&gt;        @&lt;span class="ID"&gt;Test&lt;/span&gt;&lt;span class="SPChar"&gt;(&lt;/span&gt;&lt;span class="ID"&gt;groups&lt;/span&gt; &lt;span class="SPChar"&gt;=&lt;/span&gt; &lt;span class="SPChar"&gt;{&lt;/span&gt;&lt;span class="String"&gt;"A"&lt;/span&gt;&lt;span class="SPChar"&gt;}&lt;/span&gt;&lt;span class="SPChar"&gt;,&lt;/span&gt; &lt;span class="ID"&gt;dependsOnGroups&lt;/span&gt; &lt;span class="SPChar"&gt;=&lt;/span&gt; &lt;span class="SPChar"&gt;{&lt;/span&gt;&lt;span class="String"&gt;"initA"&lt;/span&gt;&lt;span class="SPChar"&gt;}&lt;/span&gt;&lt;span class="SPChar"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   9 &lt;/span&gt;        &lt;span class="ResWord"&gt;public&lt;/span&gt; &lt;span class="ResWord"&gt;void&lt;/span&gt; &lt;span class="ID"&gt;testA&lt;/span&gt;&lt;span class="SPChar"&gt;(&lt;/span&gt;&lt;span class="SPChar"&gt;)&lt;/span&gt; &lt;span class="SPChar"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;  10 &lt;/span&gt;                &lt;span class="ID"&gt;System&lt;/span&gt;&lt;span class="SPChar"&gt;.&lt;/span&gt;&lt;span class="ID"&gt;out&lt;/span&gt;&lt;span class="SPChar"&gt;.&lt;/span&gt;&lt;span class="ID"&gt;println&lt;/span&gt;&lt;span class="SPChar"&gt;(&lt;/span&gt;&lt;span class="String"&gt;"Test A called."&lt;/span&gt;&lt;span class="SPChar"&gt;)&lt;/span&gt;&lt;span class="SPChar"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;  11 &lt;/span&gt;        &lt;span class="SPChar"&gt;}&lt;/span&gt;        &lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;  12 &lt;/span&gt;&lt;span class="SPChar"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="line874"&gt; &lt;span class="anchor" id="line-22"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-23"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line874"&gt;In class, there are two test methods: &lt;span class="anchor" id="line-24"&gt;&lt;/span&gt;&lt;/p&gt;&lt;ol type="1"&gt;&lt;li&gt;&lt;p class="line891"&gt;&lt;tt class="backtick"&gt;initA&lt;/tt&gt; - test that is supposed to make initialization of A (e.g. resource A). &lt;span class="anchor" id="line-25"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p class="line891"&gt;&lt;tt class="backtick"&gt;testA&lt;/tt&gt; - test that is supposed to perform test scenario on A; &lt;tt class="backtick"&gt;testA&lt;/tt&gt; depends on &lt;tt class="backtick"&gt;initA&lt;/tt&gt; through groups dependency. &lt;span class="anchor" id="line-26"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-27"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;Let us use following &lt;tt class="backtick"&gt;testng.xml&lt;/tt&gt;: &lt;pre id="line117"&gt;&lt;span style="font-size:100%;"&gt;&amp;lt;!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd"&gt;&lt;br /&gt;&amp;lt;suite name="NGTest"&gt;&lt;br /&gt;&amp;lt;test name="NGTest"&gt;&lt;br /&gt;       &amp;lt;groups&gt;&lt;br /&gt;               &amp;lt;run&gt;&lt;br /&gt;                       &amp;lt;include name="initA"/&gt;&lt;br /&gt;                       &amp;lt;include name="A"/&gt;&lt;br /&gt;               &amp;lt;/run&gt;&lt;br /&gt;       &amp;lt;/groups&gt;&lt;br /&gt;       &amp;lt;classes&gt;&lt;br /&gt;               &amp;lt;class name="testngtest.NGTest"/&gt;&lt;br /&gt;       &amp;lt;/classes&gt;&lt;br /&gt;&amp;lt;/test&gt;&lt;br /&gt;&amp;lt;/suite&gt;&lt;/span&gt;&lt;/pre&gt;&lt;p class="line862"&gt;&lt;span style="font-family:monospace;"&gt;&lt;span style="font-family:Georgia,serif;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-30"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-31"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-32"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-33"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-34"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-35"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-36"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-37"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-38"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-39"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-40"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-41"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-42"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-43"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-44"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line874"&gt;When tests are run, the output is, as expected: &lt;span class="anchor" id="line-47"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-48"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-49"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-50"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-51"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-52"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-53"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-54"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-55"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;Init A called.&lt;br /&gt;Test A called.&lt;br /&gt;&lt;br /&gt;===============================================&lt;br /&gt;NGTest&lt;br /&gt;Total tests run: 2, Failures: 0, Skips: 0&lt;br /&gt;===============================================&lt;/pre&gt;&lt;span class="anchor" id="line-56"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-57"&gt;&lt;/span&gt;Let us change line in &lt;tt class="backtick"&gt;testng.xml&lt;/tt&gt; to exclude &lt;tt class="backtick"&gt;initA&lt;/tt&gt; group (&lt;strong&gt;&lt;tt class="backtick"&gt;&amp;lt;exclude name="initA"/&gt;&lt;/tt&gt;&lt;/strong&gt;): &lt;pre id="line117"&gt;&lt;span style="font-size:100%;"&gt;&amp;lt;!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd"&gt;&lt;br /&gt;&amp;lt;suite name="NGTest"&gt;&lt;br /&gt;&amp;lt;test name="NGTest"&gt;&lt;br /&gt;       &amp;lt;groups&gt;&lt;br /&gt;               &amp;lt;run&gt;&lt;br /&gt;                       &amp;lt;exclude name="initA"/&gt;&lt;br /&gt;                       &amp;lt;include name="A"/&gt;&lt;br /&gt;               &amp;lt;/run&gt;&lt;br /&gt;       &amp;lt;/groups&gt;&lt;br /&gt;       &amp;lt;classes&gt;&lt;br /&gt;               &amp;lt;class name="testngtest.NGTest"/&gt;&lt;br /&gt;       &amp;lt;/classes&gt;&lt;br /&gt;&amp;lt;/test&gt;&lt;br /&gt;&amp;lt;/suite&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p class="line874"&gt;When tests are run, the output is same like in previous example: &lt;span class="anchor" id="line-75"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-76"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-77"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-78"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-79"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-80"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-81"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-82"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-83"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-84"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;Init A called.&lt;br /&gt;Test A called.&lt;br /&gt;&lt;br /&gt;===============================================&lt;br /&gt;NGTest&lt;br /&gt;Total tests run: 2, Failures: 0, Skips: 0&lt;br /&gt;===============================================&lt;/pre&gt;&lt;span class="anchor" id="line-85"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-86"&gt;&lt;/span&gt;&lt;p class="line862"&gt;From documentation to &lt;a class="http" href="http://testng.org/doc/documentation-main.html"&gt;TestNG&lt;/a&gt; I would expect that &lt;tt class="backtick"&gt;initA&lt;/tt&gt; is not called (as group is excluded) and &lt;tt class="backtick"&gt;testA&lt;/tt&gt; is skipped, since it depends on successful run of &lt;tt class="backtick"&gt;initA&lt;/tt&gt;. Instead, it looks like when &lt;tt class="backtick"&gt;testA&lt;/tt&gt; is invoked, it forces &lt;tt class="backtick"&gt;initA&lt;/tt&gt; to be called first. &lt;span class="anchor" id="line-87"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-88"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-89"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line862"&gt;Let us now disable &lt;tt class="backtick"&gt;initA&lt;/tt&gt; test in source code: &lt;span class="anchor" id="line-90"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-91"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-92"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-93"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-94"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-95"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-96"&gt;&lt;/span&gt;&lt;/p&gt;&lt;div class="codearea" dir="ltr" lang="en"&gt;&lt;pre dir="ltr" id="CA-9be0ef317c7a07afbca4045dbc9a07065eea693f" lang="en"&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   1 &lt;/span&gt;        @&lt;span class="ID"&gt;Test&lt;/span&gt;&lt;span class="SPChar"&gt;(&lt;/span&gt;&lt;span class="ID"&gt;groups&lt;/span&gt; &lt;span class="SPChar"&gt;=&lt;/span&gt; &lt;span class="SPChar"&gt;{&lt;/span&gt;&lt;span class="String"&gt;"initA"&lt;/span&gt;&lt;span class="SPChar"&gt;}&lt;/span&gt;&lt;span class="SPChar"&gt;,&lt;/span&gt; &lt;span class="ID"&gt;enabled&lt;/span&gt; &lt;span class="SPChar"&gt;=&lt;/span&gt; &lt;span class="ConsWord"&gt;false&lt;/span&gt;&lt;span class="SPChar"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   2 &lt;/span&gt;        &lt;span class="ResWord"&gt;public&lt;/span&gt; &lt;span class="ResWord"&gt;void&lt;/span&gt; &lt;span class="ID"&gt;initA&lt;/span&gt;&lt;span class="SPChar"&gt;(&lt;/span&gt;&lt;span class="SPChar"&gt;)&lt;/span&gt; &lt;span class="SPChar"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   3 &lt;/span&gt;                &lt;span class="ID"&gt;System&lt;/span&gt;&lt;span class="SPChar"&gt;.&lt;/span&gt;&lt;span class="ID"&gt;out&lt;/span&gt;&lt;span class="SPChar"&gt;.&lt;/span&gt;&lt;span class="ID"&gt;println&lt;/span&gt;&lt;span class="SPChar"&gt;(&lt;/span&gt;&lt;span class="String"&gt;"Init A called."&lt;/span&gt;&lt;span class="SPChar"&gt;)&lt;/span&gt;&lt;span class="SPChar"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   4 &lt;/span&gt;        &lt;span class="SPChar"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;span class="anchor" id="line-97"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-98"&gt;&lt;/span&gt;&lt;p class="line874"&gt;When tests are run, the output is like expected: &lt;span class="anchor" id="line-99"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-100"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-101"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-102"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-103"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-104"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-105"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;===============================================&lt;br /&gt;NGTest&lt;br /&gt;Total tests run: 1, Failures: 0, Skips: 1&lt;br /&gt;===============================================&lt;/pre&gt;&lt;span class="anchor" id="line-106"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-107"&gt;&lt;/span&gt;&lt;p class="line862"&gt;This means disabling test method in source code has "higher priority" than excluding group in &lt;tt class="backtick"&gt;testng.xml&lt;/tt&gt;. &lt;span class="anchor" id="line-108"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-109"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line862"&gt;Let us now add &lt;tt class="backtick"&gt;@BeforeClass&lt;/tt&gt; method to the &lt;tt class="backtick"&gt;NGTest&lt;/tt&gt; class and enable &lt;tt class="backtick"&gt;initA&lt;/tt&gt; in source code again. &lt;span class="anchor" id="line-110"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-111"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-112"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-113"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-114"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-115"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-116"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-117"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-118"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-119"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-120"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-121"&gt;&lt;/span&gt;&lt;/p&gt;&lt;div class="codearea" dir="ltr" lang="en"&gt;  &lt;pre dir="ltr" id="CA-8cc9054507a72b0ca690c44fc9e5542b819976d1" lang="en"&gt;&lt;span class="line"  style="font-size:85%;"&gt;&lt;span class="LineNumber"&gt; &lt;span style="font-size:100%;"&gt;  1 &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;        @&lt;/span&gt;&lt;span class="ID"  style="font-size:100%;"&gt;BeforeClass&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   2 &lt;/span&gt;        &lt;span class="ResWord"&gt;public&lt;/span&gt; &lt;span class="ResWord"&gt;void&lt;/span&gt; &lt;span class="ID"&gt;init&lt;/span&gt;&lt;span class="SPChar"&gt;(&lt;/span&gt;&lt;span class="SPChar"&gt;)&lt;/span&gt; &lt;span class="SPChar"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   3 &lt;/span&gt;                &lt;span class="ID"&gt;System&lt;/span&gt;&lt;span class="SPChar"&gt;.&lt;/span&gt;&lt;span class="ID"&gt;out&lt;/span&gt;&lt;span class="SPChar"&gt;.&lt;/span&gt;&lt;span class="ID"&gt;println&lt;/span&gt;&lt;span class="SPChar"&gt;(&lt;/span&gt;&lt;span class="String"&gt;"Init called."&lt;/span&gt;&lt;span class="SPChar"&gt;)&lt;/span&gt;&lt;span class="SPChar"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   4 &lt;/span&gt;        &lt;span class="SPChar"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   5 &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   6 &lt;/span&gt;        @&lt;span class="ID"&gt;Test&lt;/span&gt;&lt;span class="SPChar"&gt;(&lt;/span&gt;&lt;span class="ID"&gt;groups&lt;/span&gt; &lt;span class="SPChar"&gt;=&lt;/span&gt; &lt;span class="SPChar"&gt;{&lt;/span&gt;&lt;span class="String"&gt;"initA"&lt;/span&gt;&lt;span class="SPChar"&gt;}&lt;/span&gt;&lt;span class="SPChar"&gt;,&lt;/span&gt; &lt;span class="ID"&gt;enabled&lt;/span&gt; &lt;span class="SPChar"&gt;=&lt;/span&gt; &lt;span class="ConsWord"&gt;true&lt;/span&gt;&lt;span class="SPChar"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   7 &lt;/span&gt;        &lt;span class="ResWord"&gt;public&lt;/span&gt; &lt;span class="ResWord"&gt;void&lt;/span&gt; &lt;span class="ID"&gt;initA&lt;/span&gt;&lt;span class="SPChar"&gt;(&lt;/span&gt;&lt;span class="SPChar"&gt;)&lt;/span&gt; &lt;span class="SPChar"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   8 &lt;/span&gt;                &lt;span class="ID"&gt;System&lt;/span&gt;&lt;span class="SPChar"&gt;.&lt;/span&gt;&lt;span class="ID"&gt;out&lt;/span&gt;&lt;span class="SPChar"&gt;.&lt;/span&gt;&lt;span class="ID"&gt;println&lt;/span&gt;&lt;span class="SPChar"&gt;(&lt;/span&gt;&lt;span class="String"&gt;"Init A called."&lt;/span&gt;&lt;span class="SPChar"&gt;)&lt;/span&gt;&lt;span class="SPChar"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="line"  style="font-size:100%;"&gt;&lt;span class="LineNumber"&gt;   9 &lt;/span&gt;        &lt;span class="SPChar"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;span class="anchor" id="line-122"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-123"&gt;&lt;/span&gt;&lt;p class="line874"&gt;When tests are run, the output is again like expected: &lt;span class="anchor" id="line-124"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-125"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-126"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-127"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-128"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-129"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-130"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;===============================================&lt;br /&gt;NGTest&lt;br /&gt;Total tests run: 1, Failures: 0, Skips: 1&lt;br /&gt;===============================================&lt;/pre&gt;&lt;span class="anchor" id="line-131"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-132"&gt;&lt;/span&gt;Only &lt;tt class="backtick"&gt;testA&lt;/tt&gt; is included into tests, but since it depends on &lt;tt class="backtick"&gt;initA&lt;/tt&gt;, it is skipped.  &lt;span class="anchor" id="line-133"&gt;&lt;/span&gt;Same is archived when adding &lt;tt class="backtick"&gt;@BeforeTest&lt;/tt&gt; or &lt;tt class="backtick"&gt;@BeforeSuite&lt;/tt&gt;. &lt;span class="anchor" id="line-134"&gt;&lt;/span&gt;This means that &lt;tt class="backtick"&gt;exclude&lt;/tt&gt; behavior is somehow controlled by presence of &lt;tt class="backtick"&gt;@Before...&lt;/tt&gt; method(s).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4151123446707233521-7683143840988206503?l=it-bubbles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://it-bubbles.blogspot.com/feeds/7683143840988206503/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4151123446707233521&amp;postID=7683143840988206503' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4151123446707233521/posts/default/7683143840988206503'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4151123446707233521/posts/default/7683143840988206503'/><link rel='alternate' type='text/html' href='http://it-bubbles.blogspot.com/2009/01/testng-dependency-observation.html' title='TestNG dependency observation'/><author><name>Bubbles</name><uri>http://www.blogger.com/profile/06938554422574447792</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4151123446707233521.post-5694159127026627</id><published>2008-04-09T13:17:00.001-07:00</published><updated>2008-04-09T13:24:24.681-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='start'/><category scheme='http://www.blogger.com/atom/ns#' term='bubbles'/><category scheme='http://www.blogger.com/atom/ns#' term='it'/><title type='text'>IT Bubbles</title><content type='html'>This is starting post related to IT and programming that I encounter in everyday life.&lt;br /&gt;&lt;br /&gt;My intention is to blog about following areas:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Programming &lt;/li&gt;&lt;ul&gt;&lt;li&gt;Java&lt;/li&gt;&lt;li&gt;C++ (STL)&lt;/li&gt;&lt;li&gt;UML&lt;/li&gt;&lt;li&gt;Groovy, Scala&lt;/li&gt;&lt;li&gt;Python, Jython&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;IT&lt;/li&gt;&lt;ul&gt;&lt;li&gt;QA from practical point of view&lt;/li&gt;&lt;li&gt;Tools (IDE, debuggers, UML editors, ...)&lt;/li&gt;&lt;li&gt;SW development practices&lt;br /&gt;&lt;/li&gt;&lt;li&gt;etc.&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Other&lt;/li&gt;&lt;/ul&gt;I don't know how often I will blog, but I hope this is not last post :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4151123446707233521-5694159127026627?l=it-bubbles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://it-bubbles.blogspot.com/feeds/5694159127026627/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4151123446707233521&amp;postID=5694159127026627' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4151123446707233521/posts/default/5694159127026627'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4151123446707233521/posts/default/5694159127026627'/><link rel='alternate' type='text/html' href='http://it-bubbles.blogspot.com/2008/04/it-bubbles.html' title='IT Bubbles'/><author><name>Bubbles</name><uri>http://www.blogger.com/profile/06938554422574447792</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
