James Carr | Rants and Musings of an Agile Developer

Nov/06

11

Fitnesse Date Plugin

A long time ago at work I ran into a small problem during some of our tests with fitnesse. We had a few fitnesse tests that tested time based rules, stuff like a 30 day rule that basically goes something like “if the file was received longer than 60 days ago, do not process it.” This was slightly problematic though as we had to update tests from time to time if we were going to hard code dates in our tests, so I decided to take it upon myself to create a wiki plugin to generate the date time dynamically.

Anyhow, I posted it on the fitnesse mailing list on yahoogroups, but I have neglected to take the time to write up instructions on it. So, without furth ado, here it is… the fitnesse sysdate plugin how to (or you can download it now).

Installation

Installation is rather simple… simply place the SysdatePlugin.jar plugin somewhere in your class path and add it to your plugins.properties file (if you don’t already have one, create it in the same directory you run fitnesse from).

Below is what you need to add to your plugins.properties file:

WikiWidgets=fitnesse.wikitext.widgets.SystemDateWikiWidget

Start fitnesse and you’re ready to go! You should see the following at the console once fitnesse starts up.

Custom wiki widgets loaded:
                fitnesse.wikitext.widgets.SystemDateWikiWidget

Usage

Using the plugin is quite simple. To start using it right away, simply type:

!sysdate
 

This will print the current date in MMddYYYY format. If you want to use a different format, you have the option of passing the format in as a parameter… basically it will accept any format string that SimpleDateFormat takes, so refer to the api documentation for SimpleDateFormat for further information.

For example, if I wanted to print the date for today as something like 11/11/2006 21:15:23, I’d use the following:

!sysdate(MM/dd/yyyy hh:mm:ss)

Of course, it’s not usefull without features to generate a date in the future, or in the past. For example, say I want to have a test that alsways tests a boundry condition, say against a date that is always EXACTLY 30 days in the past.

The following are examples of various formats to use. Simply adding a +number or -number to the sysdate will make n days in the past or future.

!sysdate(MM/dd/yyyy hh:mm:ss)-30
!sysdate-30
!sysdate+30
!sysdate(MM/dd/yyyy hh:mm:ss)+5
 

That’s about it. If there are any features anyone like to have, please feel free to let me know. You can download the plugin here.

The Source

Here is the accompanying unit test:

package fitnesse.wikitext.widgets;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import fitnesse.wiki.*;
import fitnesse.wikitext.WidgetBuilder;

public class SystemDateWikiWidgetTest extends WidgetTest
{
    private WikiPage root;
    private PageCrawler crawler;
    private WikiPage page;
    private WidgetRoot widgetRoot;

    public void setUp() throws Exception
    {
        root = InMemoryPage.makeRoot("root");
        crawler = root.getPageCrawler();
        page = crawler.addPage(root, PathParser.parse("MyPage"));
        widgetRoot = new WidgetRoot("", page);
    }

    public void testMatches() throws Exception
    {
        assertMatches("!sysdate");
        assertMatches("!sysdate(mmmddyyyy)");
        assertMatches("!sysdate(MM-dd-yy)");
        assertMatches("!sysdate(MM-dd-yy)-1");
        assertMatches("!sysdate(MM-dd-yy)-22");
        assertMatches("!sysdate(MM-dd-yy)-300");
        assertMatches("!sysdate-30");
    }

    protected String getRegexp()
    {
        return SystemDateWikiWidget.REGEXP;
    }

    /**
     * Allow adding of sysdate special variable
     *
     * @author jamescarr
     * @throws Exception
     */
    public void testSysdateVariable() throws Exception{
        WikiPage parent = crawler.addPage(root, PathParser.parse("ParentPage"), "!sysdate\n");
        WikiPage child = crawler.addPage(parent, PathParser.parse("ChildPage"), "ick");

        WidgetRoot widgetRoot = new WidgetRoot("", child, WidgetBuilder.htmlWidgetBuilder);
        SystemDateWikiWidget w = new SystemDateWikiWidget(widgetRoot, "!sysdate");
        Date date = new Date(System.currentTimeMillis());
        SimpleDateFormat format = new SimpleDateFormat("MMddyyyy");
        System.out.println(format.format(date));
        assertEquals(format.format(date), w.render());
    }

    public void testSysdateVariableWithFormat() throws Exception{
        WikiPage parent = crawler.addPage(root, PathParser.parse("ParentPage"), "!sysdate(MMM-dd-yyyy)\n");
        WikiPage child = crawler.addPage(parent, PathParser.parse("ChildPage"), "ick");

        WidgetRoot widgetRoot = new WidgetRoot("", child, WidgetBuilder.htmlWidgetBuilder);
        SystemDateWikiWidget w = new SystemDateWikiWidget(widgetRoot, "!sysdate(MMM-dd-yyyy)");
        Date date = new Date(System.currentTimeMillis());
        SimpleDateFormat format = new SimpleDateFormat("MMM-dd-yyyy");
        System.out.println(format.format(date));
        assertEquals(format.format(date), w.render());
    }
    public void testSysdateVariableWithBadFormat() throws Exception{
        WikiPage parent = crawler.addPage(root, PathParser.parse("ParentPage"), "!define var {!sysdate(shithead)}\n");
        WikiPage child = crawler.addPage(parent, PathParser.parse("ChildPage"), "ick");

        WidgetRoot widgetRoot = new WidgetRoot("", child, WidgetBuilder.htmlWidgetBuilder);
        SystemDateWikiWidget w = new SystemDateWikiWidget(widgetRoot, "!sysdate(shithead)");

        // default
        SimpleDateFormat format = new SimpleDateFormat("MMddyyyy");
        Date date = new Date(System.currentTimeMillis());
        assertEquals( format.format(date), w.render());
    }

    public void testMultipleSysDateCalls() throws Exception{
        String dates = "!sysdate(MMMddyyyy)\n!sysdate\n!sysdate(MM-dd-yyyy)\n!sysdate(yyyy-MMM-dd)\n!sysdate(MM/dd/yyyy)";
        WikiPage parent = crawler.addPage(root, PathParser.parse("ParentPage"), dates);
        WikiPage child = crawler.addPage(parent, PathParser.parse("ChildPage"), "ick");

        WidgetRoot widgetRoot = new WidgetRoot("", child, WidgetBuilder.htmlWidgetBuilder);
        SystemDateWikiWidget w = new SystemDateWikiWidget(widgetRoot, dates);

        // expect only the first date
        SimpleDateFormat format = new SimpleDateFormat("MMMddyyyy");
        Date date = new Date(System.currentTimeMillis());

        assertEquals(format.format(date), w.render());
    }
    public void testSubtractZeroDays() throws Exception{
       String dates = "!sysdate(MM/dd/yyyy)-0";
       WikiPage parent = crawler.addPage(root, PathParser.parse("ParentPage"), dates);
       WikiPage child = crawler.addPage(parent, PathParser.parse("ChildPage"), "ick");

       WidgetRoot widgetRoot = new WidgetRoot("", child, WidgetBuilder.htmlWidgetBuilder);
       SystemDateWikiWidget w = new SystemDateWikiWidget(widgetRoot, dates);

       // expect only the first date
       SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");
       Calendar cal = Calendar.getInstance();

       Date date = new Date(System.currentTimeMillis());

       String dateString = format.format(date);
       assertEquals(dateString, w.render());
    }
    public void testSubtractTwentyDays() throws Exception{
       String dates = "!sysdate(MM/dd/yyyy)-20";
       WikiPage parent = crawler.addPage(root, PathParser.parse("ParentPage"), dates);
       WikiPage child = crawler.addPage(parent, PathParser.parse("ChildPage"), "ick");

       WidgetRoot widgetRoot = new WidgetRoot("", child, WidgetBuilder.htmlWidgetBuilder);
       SystemDateWikiWidget w = new SystemDateWikiWidget(widgetRoot, dates);

       // expect only the first date
       SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");
       Calendar cal = Calendar.getInstance();

       Date date = new Date(System.currentTimeMillis());
       cal.setTime(date);
       cal.add(Calendar.DATE, -20);
       date = cal.getTime();
       String dateString = format.format(date);
       assertEquals(dateString, w.render());
    }

    public void testAddTwentyDays() throws Exception{
       String dates = "!sysdate(MM/dd/yyyy)+20";
       WikiPage parent = crawler.addPage(root, PathParser.parse("ParentPage"), dates);
       WikiPage child = crawler.addPage(parent, PathParser.parse("ChildPage"), "ick");

       WidgetRoot widgetRoot = new WidgetRoot("", child, WidgetBuilder.htmlWidgetBuilder);
       SystemDateWikiWidget w = new SystemDateWikiWidget(widgetRoot, dates);

       // expect only the first date
       SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");
       Calendar cal = Calendar.getInstance();

       Date date = new Date(System.currentTimeMillis());
       cal.setTime(date);
       cal.add(Calendar.DATE, 20);
       date = cal.getTime();
       String dateString = format.format(date);
       assertEquals(dateString, w.render());
    }

    public void testAddThreeHundredDays() throws Exception{
       String dates = "!sysdate(MM/dd/yyyy)+300";
       WikiPage parent = crawler.addPage(root, PathParser.parse("ParentPage"), dates);
       WikiPage child = crawler.addPage(parent, PathParser.parse("ChildPage"), "ick");

       WidgetRoot widgetRoot = new WidgetRoot("", child, WidgetBuilder.htmlWidgetBuilder);
       SystemDateWikiWidget w = new SystemDateWikiWidget(widgetRoot, dates);

       // expect only the first date
       SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");
       Calendar cal = Calendar.getInstance();

       Date date = new Date(System.currentTimeMillis());
       cal.setTime(date);
       cal.add(Calendar.DATE, 300);
       date = cal.getTime();
       String dateString = format.format(date);
       assertEquals(dateString, w.render());
    }
}

And here is the source for the date widget class:

package fitnesse.wikitext.widgets;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import fitnesse.wikitext.WikiWidget;

public class SystemDateWikiWidget extends WikiWidget {
   private static final String SUBTRACT = "-";
   private static final String ADD = "+";
   private static final String DATE_FORMAT_REGEX = "\\(\\w+.*\\)";
   public static final String REGEXP = "!sysdate(\\(\\w+.*?\\))?(-\\d+|\\+\\d+)?";
   public static final Pattern pattern = Pattern.compile(REGEXP);

   private static final Pattern dateMethodPattern = Pattern.compile(DATE_FORMAT_REGEX);
   private static final Pattern subtractionPattern = Pattern.compile("(-\\d+|\\+\\d+)$");
   private String originalText = "";

   private String token = null;

   public SystemDateWikiWidget(ParentWidget parent, String text) {
      super(parent);
      originalText = text;
      Matcher match = pattern.matcher(text);
      if (match.find()) {
         token = match.group(0);
      }
   }

   private String getCurrentDate(String token) {

      SimpleDateFormat formatter = getDateFormatter(token);
      Date date = getTheDate(token);
      return formatter.format(date);
   }

   private Date getTheDate(String token) {
      Matcher match = subtractionPattern.matcher(token);
      Date date = new Date(System.currentTimeMillis());
      int days = 0;

      if (match.find()) {
         String result = match.group(0);
         if (0 == result.indexOf(SUBTRACT)) {
            days = getIntegerValue(result, SUBTRACT);
         }
         else if (0 == result.indexOf(ADD)) {
            days = getIntegerValue(result, ADD);
         }
         date = getDateDaysFromDate(date, days);
      }
      return date;
   }

   private Date getDateDaysFromDate(Date date, int days) {
      Calendar cal = Calendar.getInstance();
      cal.setTime(date);
      cal.add(Calendar.DATE, days);
      return cal.getTime();
   }

   private SimpleDateFormat getDateFormatter(String token) {
      SimpleDateFormat formatter = new SimpleDateFormat("MMddyyyy");
      Matcher match = dateMethodPattern.matcher(token);
      if (match.find()) {
         String format = match.group(0);
         format = removeParenthesis(format);
         try {
            formatter = new SimpleDateFormat(format);
         }
         catch (IllegalArgumentException e) {

         }

      }

      return formatter;
   }

   private int getIntegerValue(String result, String modifier) {
      result = result.replaceAll("\\" + modifier, "");
      return (SUBTRACT.equals(modifier)) ? -Integer.parseInt(result) : Integer.parseInt(result);
   }

   private String removeParenthesis(String format) {
      return format.replaceAll("\\(", "").replaceAll("\\)", "");
   }

   public String render() throws Exception {
      return (token != null) ? getCurrentDate(token) : originalText;
   }
}

No tags

15 comments

  • Jamie Dobson · January 26, 2007 at 5:35 am

    Hey Dude,

    So you have the source for this?

    Handy plugin, thanks, I’ll try and return the favour to the community.

    Jamie

    Reply

  • Jamie Dobson · January 26, 2007 at 5:36 am

    That was a typo, I meant to write “do you have the source”.

    Jamie

    Reply

  • Admin comment by James Carr · January 26, 2007 at 9:53 am

    Sure do… I’ll re-edit the post and make the source availble.

    Thanks,
    James

    Reply

  • Jamie Dobson · February 1, 2007 at 5:46 am

    Cheers mate.

    That is handy.

    You know, I was trawling the user group. We have done so much that I reckon, together, we could knock a cookbook out in weeks. I would happiliy contribute chapters on linking Fit with the development process, etc. You could write the plugins section. We are crying out for another book, on advance topics.

    J

    Reply

  • Peter · June 22, 2007 at 7:40 am

    I am having a few issues with the installation. The .jar is in the ClassPath, I have created the plugins.properties file and added the entry as specified above, but I get the following error when I start the server:

    Exception in thread “main” java.lang.NoClassDefFoundError: fitnesse/wikitext/WikiWidget
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at java.security.SecureClassLoader.defineClass(Unknown Source)
    at java.net.URLClassLoader.defineClass(Unknown Source)
    at java.net.URLClassLoader.access$000(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClassInternal(Unknown Source)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Unknown Source)
    at fitnesse.ComponentFactory.loadWikiWidgetPlugins(ComponentFactory.java
    :126)
    at fitnesse.FitNesse.loadContext(FitNesse.java:54)
    at fitnesse.FitNesse.main(FitNesse.java:26)

    Any ideas?

    Thanks

    Reply

  • Dava · January 17, 2008 at 6:01 am

    I got the same exception as peter. Did you know how to solve this?

    Exception in thread “main” java.lang.ClassNotFoundException: fitnesse.wikitext.w
    idgets.DateExpressionWidget
    at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
    at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:164)
    at fitnesse.ComponentFactory.loadWikiWidgetPlugins(Unknown Source)
    at fitnesse.FitNesse.loadContext(Unknown Source)
    at fitnesse.FitNesse.main(Unknown Source)

    Reply

  • James Carr · January 17, 2008 at 1:51 pm

    Hi Dava and Peter,

    The problem your having results from the plugin not being on your classpath… you must add the jar to your classpath when you run fitnesse. This can be done by adding the jar to the -cp argument in the fitnesse.bat file.

    Hope that helps!
    James

    Reply

  • Christine Mitterbauer · June 20, 2008 at 8:07 am

    Hi,
    is it possible to store the sysdate in a variable or use it within a cell of a fixture table?

    If I type in !sysdate at the beginning of a line everything works.

    Then I tried:
    !define dateVar {!sysdate}

    result:
    variable defined: dateVar=!sysdate
    (I expected: variable defined: dateVar=06202008)

    then I wanted to use !sysdate within a fixture:
    !|GuiAction|
    |action|value|result?|
    |click|BTN_TYPE_DISPO_MAIN|!sysdate|

    and sysdate was not replaced by the date..

    Do I miss something ?

    Thank you in advance
    Christine

    Reply

  • Tim Andersen · July 9, 2008 at 9:40 pm

    Wonderful plugin!

    Solves the old current-date-moving-target-testability problem!

    Thank you!

    Reply

  • Andras Hatvani · August 7, 2008 at 2:15 am

    Christine,

    I faced the same issue, because as it is stated on FitNesse.MarkupVariables:
    “The text in a variable is never interpreted as wiki markup. It is always raw literal text.”

    Andras

    Reply

  • Aristotelis · September 18, 2009 at 9:43 am

    Hi,
    Great plugin.

    We have come across a problem: the sysdate does not get intetrpreted (e.g. substituted by the date string).

    Example:
    !| Generic Fixture | ${sel} |
    | user types | !sysdate(dd/MM/yyyy) | into field | sSomeField |

    we are not sure if it is related to the Generic Fixture plugin we are using or not.
    The only occasion where we found it to work is in !Comment tables.

    Any help would be greatly appreciated.
    Thanks in advance,
    Aristotelis.

    Reply

  • Faith · January 11, 2010 at 1:16 pm

    I am using the following command line:

    java -cp fitnesse.jar fitnesse.FitNesse -jar SysdatePlugin.jar -p 8080 -e 0

    and see the following:

    FitNesse (v20090112) Started…
    port: 8080
    root page: fitnesse.wiki.FileSystemPage at ./FitNesseRoot
    logger: none
    authenticator: fitnesse.authentication.PromiscuousAuthenticator
    html page factory: fitnesse.html.HtmlPageFactory
    page version expiration set to 0 days.
    Custom wiki widgets loaded:
    fitnesse.wikitext.widgets.SystemDateWikiWidget

    which leads me to believe the plugin is installed properly.

    When I try to use the plugin in the page either in a markup variable such as:

    !define currentdate {!sysdate}

    or in a column fixture such as:

    !|StringFixture|
    |field|field?|
    |!sysdate|>>what|

    I do not see the date.

    However when I simply put

    !sysdate

    at the top of the wiki page, I see the 01112010.

    Can you help me out? I really would like to use this plugin and understand plugin’s in general.

    Thanks,
    Faith

    Reply

  • gita · July 7, 2011 at 5:14 am

    hi jamescarr,
    Plug in very good.Thank you for providing such plugins. I want datetime format as “Wednesday, July 6, 2011 8:54:42 AM EDT”. I identified the date format as “EEEE,MMMM dd,yyyy HH:mm:ss z” But i don’t have any such sort of plug-in to support the above datetime format in fitnesse. Can you please help me. Is it possible to get the above format form the existing date format. I don’t think so but doubt about it.

    Thanks in Advance James

    Regards
    gita

    Reply

  • Hitesh · August 5, 2011 at 7:45 am

    Unable to download this plugin.. has some 404 issue..

    Reply

  • Morten · January 28, 2012 at 5:06 pm

    Reply

Leave a Reply

<<

>>

Theme Design by devolux.nh2.me