How to Use It
Installation & Starting the ReFit console
- Make sure you have a Java Runtime Environment (>= 1.5) properly installed
- Download refit-0.9.zip
- Unzipping the file wherever you want results in a folder called
refit-x.y
- Start a command shell and
cd
torefit-x.y
-
Use
./refitConsole
(orrefitConsole.bat
on Windows systems) to start up the ReFit console window
Loading a FitNesse Suite
ReFit is basically a scripting environment for running ReFit scripts. The console window works exactly like
the Groovy console with the exception of an additional
ReFit menu. Try "Refit->Load Suite..." to choose (a) your current FitNesse root directory and (b) the base suite
you want to restrict your refactoring to (e.g. FitNesse
). Separate subwikis by using
a dot inbetween (e.g. FitNesse.AcceptanceTests
) or leave the base suite name empty - in that
case all tests within your FitNesse root directory will be considered.
Now ReFit will try to load all pages and sub pages of the specified suite and list all loaded pages in the
console's stdout (the lower half of the app window). Now you are ready to type in - or load - any Groovy scripts
into the upper half of the application window. ReFit will give you access to the loaded suite via the globally
visible varaible suite
.
Collecting information
Let's try our first script. Given a standard FitNesse installation (fitnesse20070619.zip), using "fitnesse/FitNesseRoot" as root dir and "FitNesse" as base suite, we try the following:
suite.pages.size()
will show "501" as result.
suite.pages.each { println it.fullName } null
will print out all 501 page names to stdout. Only the last few thousand characters will remain visible, though.
I added the null
line to make the scripts result value null
instead of the whole collection
of all pages.
Performing Refactorings
Our next task will be a refactoring: Change all fixtures named "Summary" - in other words: all tables whose first cell equals "Summary" - to "Digest":
suite.allFixtures.findAll { it.name.normalize() == "summary" }.each { fixture -> fixture.rename("Digest") }
Running this script will open the refactorings view: a table enumerating all details of refactorings which have been performed but not yet committed; in this case 5 renamings. You can now choose to rollback or commit all listed refactorings by pressing the appropriate button. Committing will, as you probably expect, change all files in the file system and print out the update, remove and move messages to stdout.
A more complex example:
suite.pages.findAll { page -> page.name.normalize() == "setup" && page.fixtures.any { it.name.normalize() == "import"} }.each {page -> page.fixtures[0].remove() }
This script will find all "SetUp" pages that also have an "Import" fixture and then remove the first fixture of those pages. Your imagination is the limit!
Capabilities
What kind of refactorings are possible? You can be derive the details from the object model described below. Among ReFit's capabilities you find:
- Renaming, moving & removing pages (test pages and suites)
- Renaming & removing fixtures (i.e. tables)
- Changing parameters of fixtures (i.e. the cells following a fixture's name)
- Renaming, deleting, moving and inserting columns in column fixtures
- Adding rows to a fixture
- Removing rows from a fixture
- Pruning empty lines from pages
- Adding and removing non-fixture lines in pages
There is much more that is not supported explicitly (yet) but can be performed by doing a bit of Groovy scripting, e.g.: Melting fixtures, renaming variables or symbols, commenting in/out lines etc.
Basic Rules
ReFit tries to keep all FitNesse syntax working, like comments (#) and literals (!- -!). Thus, a table starting with "!" will keep the "!" during refactoring.
Most of the time you can just change a property to make the refactoring work, e.g. fixture.rename("new name")
will have the same effect as fixture.name = "new name"
. This works only within the ReFit console; when you use
ReFit as an external library you should use the specialized refactoring methods like rename(..)
or fixture.replaceArgs(..)
.
Global Properties and Functions
There is one global property called suite
that will be set to an instance of FitnesseSuite
when a suite has been loaded.
There are a few global functions available in the console:
loadSuite(fitnesseRoot, suiteName)
will load a suite and return the suite object. Thesuite
property is being set implicitly.fitnesseRoot
can be a File object or a String describing the absolute path name;suiteName
is optional.reloadSuite()
will reload the current suite and get rid of all uncommitted refactorings.
Special String Methods
You have seen the normalize()
method in use. There are a few of those special String methods
available within the ReFit console:
normalize()
will remove all spaces and convert everything to lower casecleanUp()
replaces all whitespace by a single spaceisWikiName()
returns true if a string is a valid wiki name
The ReFit Object Model
There are a couple of relevant classes. Below I enumerate their public properties and methods. The specified types might or might not be statically checked - Groovy allows both ways. However, most of them are not.
refit.FitnesseSuite
Public Properties:
List<Fixture> allFixtures
: a collection of all fixtures in all pagesFile fitnessRoot
: the FitNesse root directoryList<FitnessePage> pages
: a collection of all pages in the base suiteFile suiteDir
: the directory of the given base suiteFitnessePage suitePage
: the page of the given base suite
Public Methods:
void commit()
: commit all uncommitted refactorings in this suitevoid eachFixture(Closure)
: iterate through all fixtures of all pagesvoid eachPage(Closure)
: iterate through all pagesvoid rollback()
: commit all uncommitted refactorings in this suite
refit.FitnessePage
FitnessePage
has a few subclasses which support the same interface.
Public Properties:
String name
: the page's namePageContent content
: the page's actual contentFile contentFile
: the page's content file (if there is one)List<Fixture> fixtures
: a collection of the page's fixtures in the right orderFile folder
: the page's folderString fullName
: the page's full name including all super pages up to the given root
Public Methods:
void eachFixture(Closure)
: iterate through all fixtures of this page onlyvoid moveTo(String)
: move page and all sub pages to new parentvoid remove()
: remove current page and all sub pagesvoid removeFixture(Fixture)
: remove a fixture from this pagevoid rename(String)
: give this page a new name
refit.PageContent
Public Properties:
List<Fixture> fixtures
: a collection of the page's fixtures in the right orderList flow
: a collection of Fixture and UnparsedLine instances which together form the complete contents of the pageFitnessePage page
: the page to which this content belongs
Public Methods:
void removeFromFlow(Object)
: remove a Fixture or UnparsedLine from the flow. This is a valid refactoring.void removeEmptyLinesBut(int numberOfLines)
: remove empty lines but leave up tonumberOfLines
in a row in the flow. This is a valid refactoring used to clean up pages.
refit.Fixture
Public Properties:
List<String> args
: a collection of the fixture's argumentsString canonicalName
: the fixture's normalized name (no spaces, all lower case)List columns
: a collection of the fixture's columns - if it is a column fixture - which are themselves lists of String objectsPageContent content
: the page content to which the fixture belongsboolean ignoreMarkup
: is markup being ignored in this fixture?String name
: the fixture's nameFitnessePage page
: the page object to which the fixture belongsList rows
: a collection of the fixture's rows which are themselves lists of String objects
Public Methods:
void addRow(List<String>)
: append new row to fixture. In the ReFit console you can as well just update the rows property.int countColumns()
: number of columns in this fixturevoid insertColumn(Map params)
: insert a new column. Params allowed are 'name', 'position' and 'defaultValue'; all three are optional.void remove()
: remove this fixture from its pagevoid removeColumn(String)
: remove a column by namevoid rename(String)
: give this fixture a new namevoid renameColumn(String oldName, String newName)
: rename a columnvoid replaceArgs(List<String> newArgs)
: replace existing args with new ones. In the ReFit console you can as well just update the args property.void replaceRows(List newRows)
: replace existing rows with new ones. In the ReFit console you can as well just update the rows property.
refit.UnparsedLine
Public Properties:
String text
: the actual String contents of a line. Changing this property will not automatically be considered a refactoring.
Open Issues
- No connection to any VCS so far. Checking in and out must be done manually.
- Properties of test pages are not being considered yet.
Release Notes
Version 0.9 2008-03-04
- First public version. I'm trying to collect feedback and see if ReFit is interesting to others at all.