You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2010/11/15 13:10:24 UTC
svn commit: r1035242 [2/2] - in /incubator/isis/trunk/viewer/bdd: ./
common/src/main/java/org/apache/isis/viewer/bdd/common/components/
common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/
common/src/main/java/org/apache/isis/viewer/bdd/com...
Copied: incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/SetUpObjectsForFitNesse.java (from r1034020, incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/org/apache/isis/extensions/bdd/fitnesse/internal/fixtures/SetUpObjectsForFitNesse.java)
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/SetUpObjectsForFitNesse.java?p2=incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/SetUpObjectsForFitNesse.java&p1=incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/org/apache/isis/extensions/bdd/fitnesse/internal/fixtures/SetUpObjectsForFitNesse.java&r1=1034020&r2=1035242&rev=1035242&view=diff
==============================================================================
--- incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/org/apache/isis/extensions/bdd/fitnesse/internal/fixtures/SetUpObjectsForFitNesse.java (original)
+++ incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/SetUpObjectsForFitNesse.java Mon Nov 15 12:10:23 2010
@@ -1,46 +1,44 @@
-package org.apache.isis.extensions.bdd.fitnesse.internal.fixtures;
+package net.sf.isiscontrib.bdd.fitnesse.internal.fixtures;
-import org.apache.isis.extensions.bdd.common.AliasRegistry;
-import org.apache.isis.extensions.bdd.common.CellBinding;
-import org.apache.isis.extensions.bdd.common.Constants;
-import org.apache.isis.extensions.bdd.common.fixtures.SetUpObjectsPeer;
-import org.apache.isis.extensions.bdd.common.fixtures.SetUpObjectsPeer.AssociationVisitor;
-import org.apache.isis.extensions.bdd.common.fixtures.SetUpObjectsPeer.PropertyResult;
-import org.apache.isis.extensions.bdd.common.fixtures.SetUpObjectsPeer.SetUpObjectResult;
-import org.apache.isis.extensions.bdd.common.util.Strings;
-import org.apache.isis.extensions.bdd.fitnesse.internal.AbstractFixture;
-import org.apache.isis.extensions.bdd.fitnesse.internal.CellBindingForFitNesse;
-import org.apache.isis.extensions.bdd.fitnesse.internal.util.FitnesseUtil;
-import org.apache.isis.metamodel.adapter.ObjectAdapter;
-import org.apache.isis.metamodel.spec.feature.OneToOneAssociation;
+import net.sf.isiscontrib.bdd.fitnesse.internal.AbstractFixture;
+import net.sf.isiscontrib.bdd.fitnesse.internal.CellBindingForFitNesse;
+import net.sf.isiscontrib.bdd.fitnesse.internal.util.FitnesseUtil;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.isis.viewer.bdd.common.AliasRegistry;
+import org.apache.isis.viewer.bdd.common.CellBinding;
+import org.apache.isis.viewer.bdd.common.Constants;
+import org.apache.isis.viewer.bdd.common.fixtures.SetUpObjectsPeer;
+import org.apache.isis.viewer.bdd.common.fixtures.SetUpObjectsPeer.AssociationVisitor;
+import org.apache.isis.viewer.bdd.common.fixtures.SetUpObjectsPeer.PropertyResult;
+import org.apache.isis.viewer.bdd.common.fixtures.SetUpObjectsPeer.SetUpObjectResult;
+import org.apache.isis.viewer.bdd.common.util.Strings;
import fit.Fixture;
import fit.Parse;
public class SetUpObjectsForFitNesse extends AbstractFixture<SetUpObjectsPeer> {
-
- public SetUpObjectsForFitNesse(final AliasRegistry aliasesRegistry,
- final String className, final SetUpObjectsPeer.Mode mode) {
- this(aliasesRegistry, className, mode, CellBindingForFitNesse.builder(
- Constants.ALIAS_RESULT_NAME, Constants.ALIAS_RESULT_HEAD_SET)
- .autoCreate().build());
+ public SetUpObjectsForFitNesse(final AliasRegistry aliasesRegistry, final String className,
+ final SetUpObjectsPeer.Mode mode) {
+ this(aliasesRegistry, className, mode, CellBindingForFitNesse
+ .builder(Constants.ALIAS_RESULT_NAME, Constants.ALIAS_RESULT_HEAD_SET).autoCreate().build());
}
- private SetUpObjectsForFitNesse(final AliasRegistry aliasesRegistry,
- final String className, final SetUpObjectsPeer.Mode mode,
- final CellBinding aliasBinding) {
+ private SetUpObjectsForFitNesse(final AliasRegistry aliasesRegistry, final String className,
+ final SetUpObjectsPeer.Mode mode, final CellBinding aliasBinding) {
super(new SetUpObjectsPeer(aliasesRegistry, className, mode, aliasBinding));
}
@Override
public void doTable(final Parse table) {
- final Parse classNameCell = table.parts.parts.more;
- if (getPeer().isSpecOk()) {
+ final Parse classNameCell = table.parts.parts.more;
+ if (getPeer().isSpecOk()) {
FitnesseUtil.exception(this, classNameCell, "(no such class)");
return;
- }
+ }
super.doTable(table);
@@ -52,47 +50,45 @@ public class SetUpObjectsForFitNesse ext
}
}
- @Override
+ @Override
protected void doRowsWithBindings(final Parse headRow) {
Parse eachHead = headRow.parts;
try {
- eachHead = addProperties(eachHead);
+ eachHead = addProperties(eachHead);
} catch (final Throwable throwable) {
exception(eachHead, throwable);
}
}
- private Parse addProperties(Parse eachHead) {
- for (int i = 0; eachHead != null; i++, eachHead = eachHead.more) {
- final String headText = eachHead.text();
-
- PropertyResult status = addProperty(headText, i);
- if (!status.isOk()) {
- if (status == PropertyResult.NO_SUCH_PROPERTY) {
- FitnesseUtil
- .exception(this, eachHead, "(no such property)");
- } else
- if (status == PropertyResult.NOT_A_PROPERTY) {
- FitnesseUtil.exception(this, eachHead, "(not a property)");
- }
- }
- }
- return eachHead;
- }
-
- private PropertyResult addProperty(final String heading, int colNum) {
- return getPeer().definePropertyOrAlias(heading, colNum);
- }
+ private Parse addProperties(Parse eachHead) {
+ for (int i = 0; eachHead != null; i++, eachHead = eachHead.more) {
+ final String headText = eachHead.text();
+
+ PropertyResult status = addProperty(headText, i);
+ if (!status.isOk()) {
+ if (status == PropertyResult.NO_SUCH_PROPERTY) {
+ FitnesseUtil.exception(this, eachHead, "(no such property)");
+ } else if (status == PropertyResult.NOT_A_PROPERTY) {
+ FitnesseUtil.exception(this, eachHead, "(not a property)");
+ }
+ }
+ }
+ return eachHead;
+ }
+
+ private PropertyResult addProperty(final String heading, int colNum) {
+ return getPeer().definePropertyOrAlias(heading, colNum);
+ }
@Override
public void doRow(final Parse row) {
// copied down from superclass
clearCellTextList();
-
+
doCells(row.parts);
if (!getPeer().getAliasBinding().isFound()) {
- addCellText(""); // placeholder
+ addCellText(""); // placeholder
}
try {
@@ -104,58 +100,58 @@ public class SetUpObjectsForFitNesse ext
}
}
- private void clearCellTextList() {
- getPeer().resetForNextObject();
- }
-
- private boolean addCellText(String cellText) {
- return getPeer().addPropertyValueOrAlias(cellText);
- }
+ private void clearCellTextList() {
+ getPeer().resetForNextObject();
+ }
+
+ private boolean addCellText(String cellText) {
+ return getPeer().addPropertyValueOrAlias(cellText);
+ }
@Override
public void doCell(final Parse cell, final int column) {
super.doCell(cell, column);
String cellText = cell.text();
- addCellText(cellText);
+ addCellText(cellText);
}
private void setUpObject(final Parse row) throws Exception {
- // spec for adapter should have been initialized in doTable.
+ // spec for adapter should have been initialized in doTable.
final ObjectAdapter adapter = getPeer().createInstance();
if (adapter == null) {
- return;
+ return;
}
// TODO: redo using createObject(), catch the StoryFailureException instead
for (int colNum = 0; colNum < getPeer().getProperties().size(); colNum++) {
-
- SetUpObjectResult result = getPeer().setUpProperty(adapter, colNum);
-
+
+ SetUpObjectResult result = getPeer().setUpProperty(adapter, colNum);
+
if (!result.isHandled()) {
- final Parse cell = FitnesseUtil.cell(row, colNum);
- if (result.isError()) {
- FitnesseUtil.exception(this, cell, result.getErrorMessage());
- } else {
- right(cell);
- }
+ final Parse cell = FitnesseUtil.cell(row, colNum);
+ if (result.isError()) {
+ FitnesseUtil.exception(this, cell, result.getErrorMessage());
+ } else {
+ right(cell);
+ }
}
}
getPeer().persistIfNecessary(adapter);
if (getPeer().getAliasBinding().isFound()) {
- // TODO: review - this block may be redundant since the peer now captures
- // the alias when the text is added (earlier).
+ // TODO: review - this block may be redundant since the peer now captures
+ // the alias when the text is added (earlier).
int aliasColumn = getPeer().getAliasBinding().getColumn();
- final Parse aliasCell = FitnesseUtil.cell(row, aliasColumn);
+ final Parse aliasCell = FitnesseUtil.cell(row, aliasColumn);
if (aliasCell != null) {
String alias = aliasCell.text();
if (Strings.emptyString(alias)) {
alias = getPeer().aliasFor(adapter);
FitnesseUtil.setBody(aliasCell, Fixture.gray(alias));
} else {
- getPeer().getAliasRegistry().aliasAs(alias, adapter);
+ getPeer().getAliasRegistry().aliasAs(alias, adapter);
}
}
} else {
@@ -164,26 +160,28 @@ public class SetUpObjectsForFitNesse ext
}
}
- private void rightAllKnownAssociationCells(final Parse row) {
- getPeer().forEachAssociation(new AssociationVisitor() {
- public void visit(OneToOneAssociation association, int i) {
- final Parse cell = FitnesseUtil.cell(row, i);
- if (cell != null) {
- right(cell);
- }
- }
- });
+ private void rightAllKnownAssociationCells(final Parse row) {
+ getPeer().forEachAssociation(new AssociationVisitor() {
+ @Override
+ public void visit(OneToOneAssociation association, int i) {
+ final Parse cell = FitnesseUtil.cell(row, i);
+ if (cell != null) {
+ right(cell);
+ }
+ }
+ });
}
private void wrongAllKnownAssociationCells(final Parse row) {
- getPeer().forEachAssociation(new AssociationVisitor() {
- public void visit(OneToOneAssociation association, int i) {
- final Parse cell = FitnesseUtil.cell(row, i);
- if (cell != null) {
- wrong(cell);
- }
- }
- });
+ getPeer().forEachAssociation(new AssociationVisitor() {
+ @Override
+ public void visit(OneToOneAssociation association, int i) {
+ final Parse cell = FitnesseUtil.cell(row, i);
+ if (cell != null) {
+ wrong(cell);
+ }
+ }
+ });
}
private Parse appendCell(final Parse row, final String text) {
Copied: incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/UsingIsisViewerForFitNesse.java (from r1034020, incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/org/apache/isis/extensions/bdd/fitnesse/internal/fixtures/UsingNakedObjectsViewerForFitNesse.java)
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/UsingIsisViewerForFitNesse.java?p2=incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/UsingIsisViewerForFitNesse.java&p1=incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/org/apache/isis/extensions/bdd/fitnesse/internal/fixtures/UsingNakedObjectsViewerForFitNesse.java&r1=1034020&r2=1035242&rev=1035242&view=diff
==============================================================================
--- incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/org/apache/isis/extensions/bdd/fitnesse/internal/fixtures/UsingNakedObjectsViewerForFitNesse.java (original)
+++ incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/UsingIsisViewerForFitNesse.java Mon Nov 15 12:10:23 2010
@@ -1,167 +1,145 @@
-package org.apache.isis.extensions.bdd.fitnesse.internal.fixtures;
+package net.sf.isiscontrib.bdd.fitnesse.internal.fixtures;
import java.util.ArrayList;
import java.util.List;
-import org.apache.isis.extensions.bdd.common.AliasRegistry;
-import org.apache.isis.extensions.bdd.common.CellBinding;
-import org.apache.isis.extensions.bdd.common.Constants;
-import org.apache.isis.extensions.bdd.common.StoryBoundValueException;
-import org.apache.isis.extensions.bdd.common.StoryCell;
-import org.apache.isis.extensions.bdd.common.fixtures.UsingNakedObjectsViewerPeer;
-import org.apache.isis.extensions.bdd.common.fixtures.perform.Perform;
-import org.apache.isis.extensions.bdd.fitnesse.internal.AbstractFixture;
-import org.apache.isis.extensions.bdd.fitnesse.internal.CellBindingForFitNesse;
-import org.apache.isis.extensions.bdd.fitnesse.internal.fixtures.perform.StoryCellForFitNesse;
-import org.apache.isis.extensions.bdd.fitnesse.internal.util.FitnesseUtil;
-import org.apache.isis.metamodel.adapter.ObjectAdapter;
-import org.apache.isis.metamodel.spec.feature.ObjectMember;
+import net.sf.isiscontrib.bdd.fitnesse.internal.AbstractFixture;
+import net.sf.isiscontrib.bdd.fitnesse.internal.CellBindingForFitNesse;
+import net.sf.isiscontrib.bdd.fitnesse.internal.fixtures.perform.StoryCellForFitNesse;
+import net.sf.isiscontrib.bdd.fitnesse.internal.util.FitnesseUtil;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.viewer.bdd.common.AliasRegistry;
+import org.apache.isis.viewer.bdd.common.CellBinding;
+import org.apache.isis.viewer.bdd.common.Constants;
+import org.apache.isis.viewer.bdd.common.StoryBoundValueException;
+import org.apache.isis.viewer.bdd.common.StoryCell;
+import org.apache.isis.viewer.bdd.common.fixtures.UsingIsisViewerPeer;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.Perform;
import com.google.common.collect.Lists;
import fit.Parse;
import fit.exception.FitFailureException;
-public class UsingNakedObjectsViewerForFitNesse extends
- AbstractFixture<UsingNakedObjectsViewerPeer> {
+public class UsingIsisViewerForFitNesse extends AbstractFixture<UsingIsisViewerPeer> {
- private final List<Parse> argumentCells = new ArrayList<Parse>();
-
- public UsingNakedObjectsViewerForFitNesse(
- final AliasRegistry aliasesRegistry,
- final Perform.Mode mode) {
- this(aliasesRegistry, mode,
- CellBindingForFitNesse.builder(
- Constants.ON_OBJECT_NAME, Constants.ON_OBJECT_HEAD_SET)
- .ditto().build(),
- CellBindingForFitNesse.builder(
- Constants.ALIAS_RESULT_NAME, Constants.ALIAS_RESULT_HEAD_SET)
- .optional().build(),
- CellBindingForFitNesse.builder(
- Constants.PERFORM_NAME, Constants.PERFORM_HEAD_SET)
- .ditto().build(),
- CellBindingForFitNesse.builder(
- Constants.ON_MEMBER_NAME, Constants.ON_MEMBER_HEAD_SET)
- .optional().build(),
- CellBindingForFitNesse.builder(
- Constants.THAT_IT_NAME, Constants.THAT_IT_HEAD_SET)
- .ditto().optional().build(),
- CellBindingForFitNesse.builder(
- Constants.WITH_ARGUMENTS_NAME, Constants.WITH_ARGUMENTS_HEAD_SET)
- .optional().build());
- }
-
- private UsingNakedObjectsViewerForFitNesse(
- final AliasRegistry aliasesRegistry,
- final Perform.Mode mode,
- final CellBinding onObjectBinding,
- final CellBinding aliasResultAsBinding,
- final CellBinding performBinding,
- final CellBinding onMemberBinding, final CellBinding thatItBinding,
- final CellBinding arg0Binding) {
- super(new UsingNakedObjectsViewerPeer(aliasesRegistry, mode,
- onObjectBinding, aliasResultAsBinding, performBinding,
- onMemberBinding, thatItBinding, arg0Binding));
- }
-
- @Override
- public void doRows(final Parse headRow) {
- super.doRows(headRow);
- ensureArgBindingLast();
- }
-
- private void ensureArgBindingLast() {
- if (getPeer().isArg0BindingLast()) {
- return;
- }
- FitnesseUtil.exception(this, getPeer().getArg0Binding().getHeadCell(),
- "(must be last column)");
- throw new FitFailureException("(invalid binding order)");
- }
-
- @Override
- public void doRow(final Parse row) {
- argumentCells.clear();
- super.doRow(row);
- execute();
- }
-
- @Override
- public void doCell(final Parse cell, final int columnNum) {
- super.doCell(cell, columnNum);
-
- captureArgumentCellsIfAny(cell, columnNum);
- }
-
- private void captureArgumentCellsIfAny(final Parse cell, final int columnNum) {
- if (!getPeer().getArg0Binding().isFound()) {
- return;
- }
- if (columnNum < getPeer().getArg0Binding().getColumn()) {
- return;
- }
- argumentCells.add(cell);
- }
-
- private void execute() {
-
- ObjectAdapter onAdapter = null;
- try {
- onAdapter = getPeer().validateOnObject();
- } catch (StoryBoundValueException ex) {
- FitnesseUtil.exception(this, ex);
- return;
- }
-
- String aliasAs = null;
- try {
- aliasAs = getPeer().validateAliasAs();
- } catch (StoryBoundValueException ex) {
- FitnesseUtil.exception(this, ex);
- return;
- }
-
- Perform performCommand = null;
- try {
- performCommand = getPeer().validatePerform();
- } catch (StoryBoundValueException ex) {
- FitnesseUtil.exception(this, ex);
- return;
- }
-
- ObjectMember nakedObjectMember = null;
- if (performCommand.requiresMember()) {
- try {
- nakedObjectMember = getPeer().validateOnMember(onAdapter);
- } catch (StoryBoundValueException ex) {
- FitnesseUtil.exception(this, ex);
- return;
- }
- }
-
- try {
- List<StoryCell> argumentStoryCells = asValues(argumentCells);
- getPeer().performCommand(performCommand, onAdapter, nakedObjectMember,
- argumentStoryCells, aliasAs);
- } catch (StoryBoundValueException ex) {
- FitnesseUtil.exception(this, ex);
- return;
- }
-
- rightMemberElseOnObjectCell();
- }
-
- private static List<StoryCell> asValues(List<Parse> argCells) {
- List<StoryCell> storyValues = Lists.newArrayList();
- for (Parse parse : argCells) {
- storyValues.add(new StoryCellForFitNesse(parse));
- }
- return storyValues;
- }
-
- private void rightMemberElseOnObjectCell() {
- right(getPeer().getMemberElseOnObjectCell());
- }
+ private final List<Parse> argumentCells = new ArrayList<Parse>();
+ public UsingIsisViewerForFitNesse(final AliasRegistry aliasesRegistry, final Perform.Mode mode) {
+ this(aliasesRegistry, mode, CellBindingForFitNesse
+ .builder(Constants.ON_OBJECT_NAME, Constants.ON_OBJECT_HEAD_SET).ditto().build(), CellBindingForFitNesse
+ .builder(Constants.ALIAS_RESULT_NAME, Constants.ALIAS_RESULT_HEAD_SET).optional().build(),
+ CellBindingForFitNesse.builder(Constants.PERFORM_NAME, Constants.PERFORM_HEAD_SET).ditto().build(),
+ CellBindingForFitNesse.builder(Constants.ON_MEMBER_NAME, Constants.ON_MEMBER_HEAD_SET).optional().build(),
+ CellBindingForFitNesse.builder(Constants.THAT_IT_NAME, Constants.THAT_IT_HEAD_SET).ditto().optional()
+ .build(), CellBindingForFitNesse
+ .builder(Constants.WITH_ARGUMENTS_NAME, Constants.WITH_ARGUMENTS_HEAD_SET).optional().build());
+ }
+
+ private UsingIsisViewerForFitNesse(final AliasRegistry aliasesRegistry, final Perform.Mode mode,
+ final CellBinding onObjectBinding, final CellBinding aliasResultAsBinding, final CellBinding performBinding,
+ final CellBinding onMemberBinding, final CellBinding thatItBinding, final CellBinding arg0Binding) {
+ super(new UsingIsisViewerPeer(aliasesRegistry, mode, onObjectBinding, aliasResultAsBinding, performBinding,
+ onMemberBinding, thatItBinding, arg0Binding));
+ }
+
+ @Override
+ public void doRows(final Parse headRow) {
+ super.doRows(headRow);
+ ensureArgBindingLast();
+ }
+
+ private void ensureArgBindingLast() {
+ if (getPeer().isArg0BindingLast()) {
+ return;
+ }
+ FitnesseUtil.exception(this, getPeer().getArg0Binding().getHeadCell(), "(must be last column)");
+ throw new FitFailureException("(invalid binding order)");
+ }
+
+ @Override
+ public void doRow(final Parse row) {
+ argumentCells.clear();
+ super.doRow(row);
+ execute();
+ }
+
+ @Override
+ public void doCell(final Parse cell, final int columnNum) {
+ super.doCell(cell, columnNum);
+
+ captureArgumentCellsIfAny(cell, columnNum);
+ }
+
+ private void captureArgumentCellsIfAny(final Parse cell, final int columnNum) {
+ if (!getPeer().getArg0Binding().isFound()) {
+ return;
+ }
+ if (columnNum < getPeer().getArg0Binding().getColumn()) {
+ return;
+ }
+ argumentCells.add(cell);
+ }
+
+ private void execute() {
+
+ ObjectAdapter onAdapter = null;
+ try {
+ onAdapter = getPeer().validateOnObject();
+ } catch (StoryBoundValueException ex) {
+ FitnesseUtil.exception(this, ex);
+ return;
+ }
+
+ String aliasAs = null;
+ try {
+ aliasAs = getPeer().validateAliasAs();
+ } catch (StoryBoundValueException ex) {
+ FitnesseUtil.exception(this, ex);
+ return;
+ }
+
+ Perform performCommand = null;
+ try {
+ performCommand = getPeer().validatePerform();
+ } catch (StoryBoundValueException ex) {
+ FitnesseUtil.exception(this, ex);
+ return;
+ }
+
+ ObjectMember nakedObjectMember = null;
+ if (performCommand.requiresMember()) {
+ try {
+ nakedObjectMember = getPeer().validateOnMember(onAdapter);
+ } catch (StoryBoundValueException ex) {
+ FitnesseUtil.exception(this, ex);
+ return;
+ }
+ }
+
+ try {
+ List<StoryCell> argumentStoryCells = asValues(argumentCells);
+ getPeer().performCommand(performCommand, onAdapter, nakedObjectMember, argumentStoryCells, aliasAs);
+ } catch (StoryBoundValueException ex) {
+ FitnesseUtil.exception(this, ex);
+ return;
+ }
+
+ rightMemberElseOnObjectCell();
+ }
+
+ private static List<StoryCell> asValues(List<Parse> argCells) {
+ List<StoryCell> storyValues = Lists.newArrayList();
+ for (Parse parse : argCells) {
+ storyValues.add(new StoryCellForFitNesse(parse));
+ }
+ return storyValues;
+ }
+
+ private void rightMemberElseOnObjectCell() {
+ right(getPeer().getMemberElseOnObjectCell());
+ }
}
Copied: incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/perform/StoryCellForFitNesse.java (from r1034020, incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/org/apache/isis/extensions/bdd/fitnesse/internal/fixtures/perform/StoryCellForFitNesse.java)
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/perform/StoryCellForFitNesse.java?p2=incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/perform/StoryCellForFitNesse.java&p1=incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/org/apache/isis/extensions/bdd/fitnesse/internal/fixtures/perform/StoryCellForFitNesse.java&r1=1034020&r2=1035242&rev=1035242&view=diff
==============================================================================
--- incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/org/apache/isis/extensions/bdd/fitnesse/internal/fixtures/perform/StoryCellForFitNesse.java (original)
+++ incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/perform/StoryCellForFitNesse.java Mon Nov 15 12:10:23 2010
@@ -1,33 +1,36 @@
-package org.apache.isis.extensions.bdd.fitnesse.internal.fixtures.perform;
+package net.sf.isiscontrib.bdd.fitnesse.internal.fixtures.perform;
-import org.apache.isis.extensions.bdd.common.StoryCell;
+import org.apache.isis.viewer.bdd.common.StoryCell;
import fit.Parse;
public class StoryCellForFitNesse implements StoryCell {
-
- private Parse source;
- public StoryCellForFitNesse(Parse source) {
- this.source = source;
- }
-
- public String getText() {
- return source.text();
- }
-
- /**
- * The implementation-specific representation of this text.
- *
- * <p>
- * Holds a Fit {@link Parse} object.
- */
- public Object getSource() {
- return source;
- }
-
- public void setText(String str) {
- source.body = str;
- }
-
+ private final Parse source;
+
+ public StoryCellForFitNesse(Parse source) {
+ this.source = source;
+ }
+
+ @Override
+ public String getText() {
+ return source.text();
+ }
+
+ /**
+ * The implementation-specific representation of this text.
+ *
+ * <p>
+ * Holds a Fit {@link Parse} object.
+ */
+ @Override
+ public Object getSource() {
+ return source;
+ }
+
+ @Override
+ public void setText(String str) {
+ source.body = str;
+ }
+
}
Modified: incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/util/Aliases.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/util/Aliases.java?rev=1035242&r1=1034020&r2=1035242&view=diff
==============================================================================
--- incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/util/Aliases.java (original)
+++ incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/util/Aliases.java Mon Nov 15 12:10:23 2010
@@ -1,18 +1,19 @@
-package org.apache.isis.extensions.bdd.fitnesse.internal.util;
+package net.sf.isiscontrib.bdd.fitnesse.internal.util;
-import org.apache.isis.extensions.bdd.common.CellBinding;
-import org.apache.isis.extensions.bdd.common.Constants;
-import org.apache.isis.extensions.bdd.fitnesse.internal.fixtures.perform.StoryCellForFitNesse;
+import net.sf.isiscontrib.bdd.fitnesse.internal.fixtures.perform.StoryCellForFitNesse;
+
+import org.apache.isis.viewer.bdd.common.CellBinding;
+import org.apache.isis.viewer.bdd.common.Constants;
import fit.Fixture;
import fit.Parse;
public final class Aliases {
- private Aliases() {}
+ private Aliases() {
+ }
- public static CellBinding findOrCreateAlias(final Parse heads,
- final CellBinding aliasInfo) {
+ public static CellBinding findOrCreateAlias(final Parse heads, final CellBinding aliasInfo) {
Parse eachHead = heads;
for (int i = 0; eachHead != null; i++, eachHead = eachHead.more) {
final String headText = eachHead.body;
@@ -26,8 +27,7 @@ public final class Aliases {
// Append an alias cell to header
if (!aliasInfo.isFound()) {
final int size = heads.size();
- final Parse aliasCell = new Parse("td", Fixture
- .gray(Constants.ALIAS_RESULT_HEAD), null, null);
+ final Parse aliasCell = new Parse("td", Fixture.gray(Constants.ALIAS_RESULT_HEAD), null, null);
heads.last().more = aliasCell;
aliasInfo.create(size, new StoryCellForFitNesse(aliasCell));
}
@@ -35,8 +35,7 @@ public final class Aliases {
}
public static boolean hasAliasText(final String headText) {
- return Constants.ALIAS_RESULT_HEAD_ALT1.equals(headText)
- || Constants.ALIAS_RESULT_HEAD.equals(headText);
+ return Constants.ALIAS_RESULT_HEAD_ALT1.equals(headText) || Constants.ALIAS_RESULT_HEAD.equals(headText);
}
}
Modified: incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/util/FitnesseUtil.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/util/FitnesseUtil.java?rev=1035242&r1=1034020&r2=1035242&view=diff
==============================================================================
--- incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/util/FitnesseUtil.java (original)
+++ incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/util/FitnesseUtil.java Mon Nov 15 12:10:23 2010
@@ -1,7 +1,7 @@
-package org.apache.isis.extensions.bdd.fitnesse.internal.util;
+package net.sf.isiscontrib.bdd.fitnesse.internal.util;
-import org.apache.isis.extensions.bdd.common.StoryBoundValueException;
-import org.apache.isis.extensions.bdd.common.StoryCell;
+import org.apache.isis.viewer.bdd.common.StoryBoundValueException;
+import org.apache.isis.viewer.bdd.common.StoryCell;
import fit.Fixture;
import fit.Parse;
@@ -9,51 +9,49 @@ import fit.exception.FitFailureException
public final class FitnesseUtil {
- private FitnesseUtil() {
- }
+ private FitnesseUtil() {
+ }
- public static Parse cell(final Parse row, final int columnNumber) {
- Parse cell = row.parts;
- if (cell == null) {
- return cell;
- }
- int i = 0;
- while (true) {
- if (columnNumber == i) {
- return cell;
- }
- if (cell.more == null) {
- return cell;
- }
- cell = cell.more;
- i++;
- }
- }
-
- public static void exception(Fixture fixture, StoryBoundValueException ex) {
- exception(fixture, ex.getStoryCell(), ex.getMessage());
- }
-
- public static void exception(final Fixture fixture,
- final StoryCell storyCell, final String message) {
- exception(fixture, asParse(storyCell), message);
- }
-
- public static Parse asParse(final StoryCell storyValue) {
- return (Parse) storyValue.getSource();
- }
-
- public static void exception(final Fixture fixture, final Parse cell,
- final String message) {
- fixture.exception(cell, new FitFailureException(message));
- }
-
- public static void setText(StoryCell storyCell, String str) {
- storyCell.setText(str);
- }
-
- public static void setBody(Parse parse, String str) {
- parse.body = str;
- }
+ public static Parse cell(final Parse row, final int columnNumber) {
+ Parse cell = row.parts;
+ if (cell == null) {
+ return cell;
+ }
+ int i = 0;
+ while (true) {
+ if (columnNumber == i) {
+ return cell;
+ }
+ if (cell.more == null) {
+ return cell;
+ }
+ cell = cell.more;
+ i++;
+ }
+ }
+
+ public static void exception(Fixture fixture, StoryBoundValueException ex) {
+ exception(fixture, ex.getStoryCell(), ex.getMessage());
+ }
+
+ public static void exception(final Fixture fixture, final StoryCell storyCell, final String message) {
+ exception(fixture, asParse(storyCell), message);
+ }
+
+ public static Parse asParse(final StoryCell storyValue) {
+ return (Parse) storyValue.getSource();
+ }
+
+ public static void exception(final Fixture fixture, final Parse cell, final String message) {
+ fixture.exception(cell, new FitFailureException(message));
+ }
+
+ public static void setText(StoryCell storyCell, String str) {
+ storyCell.setText(str);
+ }
+
+ public static void setBody(Parse parse, String str) {
+ parse.body = str;
+ }
}
Modified: incubator/isis/trunk/viewer/bdd/fitnesse/src/site/site.xml
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/viewer/bdd/fitnesse/src/site/site.xml?rev=1035242&r1=1035241&r2=1035242&view=diff
==============================================================================
--- incubator/isis/trunk/viewer/bdd/fitnesse/src/site/site.xml (original)
+++ incubator/isis/trunk/viewer/bdd/fitnesse/src/site/site.xml Mon Nov 15 12:10:23 2010
@@ -1,26 +1,15 @@
<project name="${project.name}">
<version position="right"/>
+
<bannerLeft>
<name>HAL</name>
<src>images/hal-logo-for-maven-site.jpg</src>
<href>.</href>
</bannerLeft>
- <poweredBy>
- <logo name="Naked Objects" href="http://nakedobjects.org"
- img="images/NO-powered-by-logo.png"/>
- </poweredBy>
-
- <skin>
- <groupId>org.starobjects.star</groupId>
- <artifactId>skin</artifactId>
- <version>1</version>
- </skin>
-
<body>
<links>
- <item name="Naked Objects" href="http://www.nakedobjects.org/"/>
- <item name="Sister Projects" href="http://starobjects.sourceforge.net"/>
+ <item name="Apache Isis" href="http://incubator.apache.org/isis/"/>
</links>
<menu ref="modules" inherit="top"/>
@@ -28,26 +17,17 @@
<menu name="Documentation">
<item name="User Guide (PDF)" href="documentation/docbkx/pdf/user-guide.pdf"/>
<item name="User Guide (HTML)" href="documentation/docbkx/html/user-guide/user-guide.html"/>
- <item name="Developers Guide (PDF)" href="documentation/docbkx/pdf/developers-guide.pdf"/>
- <item name="Developers Guide (HTML)" href="documentation/docbkx/html/developers-guide/developers-guide.html"/>
</menu>
<menu name="Project Resources">
- <item name="SF Trac Wiki" href="http://sourceforge.net/apps/trac/testedobjects"/>
- <item name="SF Project Page" href="http://sourceforge.net/projects/testedobjects"/>
- </menu>
-
- <menu name="Sister Projects">
- <item name="Home" href="http://starobjects.sourceforge.net"/>
- <item name="Maven Repo" href="http://starobjects.sourceforge.net/m2-repo"/>
+ <item name="SF Trac Wiki" href="http://sourceforge.net/apps/trac/isis-contrib"/>
+ <item name="SF Project Page" href="http://sourceforge.net/projects/isis-contrib"/>
+ <item name="Maven Repo" href="http://isis-contrib.sourceforge.net/m2-repo"/>
</menu>
<menu name="Other Resources">
<item name="Dan Haywood's blog" href="http://danhaywood.com"/>
- <item name="Naked Objects" href="http://www.nakedobjects.org"/>
- <item name="Naked Objects blog" href="http://blog.nakedobjects.org"/>
- <item name="Naked Objects for .NET" href="http://www.nakedobjects.net"/>
- <item name="Scimpi" href="http://www.scimpi.org/" />
+ <item name="DDD using Naked Objects" href="http://www.pragprog/com/titles/dhnako" />
</menu>
</body>
Modified: incubator/isis/trunk/viewer/bdd/pom.xml
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/viewer/bdd/pom.xml?rev=1035242&r1=1035241&r2=1035242&view=diff
==============================================================================
--- incubator/isis/trunk/viewer/bdd/pom.xml (original)
+++ incubator/isis/trunk/viewer/bdd/pom.xml Mon Nov 15 12:10:23 2010
@@ -101,9 +101,7 @@
<modules>
<module>common</module>
<module>concordion</module>
- <!--
<module>fitnesse</module>
- -->
</modules>
</project>
Modified: incubator/isis/trunk/viewer/bdd/src/docbkx/guide/isis-bdd-viewer.xml
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/viewer/bdd/src/docbkx/guide/isis-bdd-viewer.xml?rev=1035242&r1=1035241&r2=1035242&view=diff
==============================================================================
--- incubator/isis/trunk/viewer/bdd/src/docbkx/guide/isis-bdd-viewer.xml (original)
+++ incubator/isis/trunk/viewer/bdd/src/docbkx/guide/isis-bdd-viewer.xml Mon Nov 15 12:10:23 2010
@@ -1,10 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
-"file:./resources/docbook-xml-4.5/docbookx.dtd">
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+"file:./resources/docbook-xml-4.5/docbookx.dtd">
<book>
<bookinfo>
<title><?eval ${docbkxGuideTitle}?></title>
+
<subtitle><?eval ${docbkxGuideSubTitle}?></subtitle>
+
<releaseinfo><?eval ${project.version}?></releaseinfo>
<authorgroup>
@@ -29,29 +31,32 @@
<preface id="preface">
<title>Preface</title>
- <para><ulink url="http://starobjects.org">Tested Objects</ulink> is a
- sister project for the <ulink url="http://nakedobjects.org">Naked
- Objects</ulink> framework, providing integration with <ulink
- url="http://fitnesse.org">FitNesse</ulink> to enable agile acceptance (or
- scenario) testing. The integration bundles the FitNesse wiki server and
- provides a set of generic Fitnesse fixtures that interact with the domain
- model in the same manner that a Naked Objects viewer does. It also
- provides a Maven archetype to get you started quickly.</para>
-
- <para>This user guide describes how to use Tested Objects to test your
- Naked Objects domain applications through FitNesse. Much of this guidance
- relates to using the Maven archetype; for ease of reference the archetype
- creates an online user guide within the FitNesse wiki. If you are
- interested in building Tested Objects from source code (perhaps with a
- view to contributing to or extending the capabilities of Tested Objects
- itself) then please see the developers' guide.</para>
-
- <para>Tested Objects is hosted on <ulink
- url="http://testedobjects.sourceforge.net">SourceForge</ulink>, and is
- licensed under <ulink
+ <para>Behaviour-driven development is a means to drive the development of
+ an application through stories and scenarios. These are expressed in a
+ semi-formal textual form that can be understood (or indeed be written) by
+ the domain expert/business analyst, but which can then be used to directly
+ exercise the system under test as it is developed.</para>
+
+ <para>A number of frameworks exist to streamline this process. Generally
+ these require the developer to write glue code that acts as a bridge from
+ the textual specification and the system under test.</para>
+
+ <para>The <emphasis>BDD Viewer</emphasis> module for <emphasis>Apache
+ Isis</emphasis> aims to allow <acronym>BDD</acronym> stories/scenarios to
+ be written against the domain model of an Isis application, without the
+ developer having to write any glue code. It consists of a common library
+ that abstracts the interaction with the Isis metamodel, along with an
+ integration (that uses this common library) for one particular
+ <acronym>BDD</acronym> framework, namely <ulink
+ url="http://concordion.org">Concordion</ulink>.</para>
+
+ <para>This user guide describes how to use the Concordion integration,
+ along with details of the common library so that other BDD frameworks can
+ be integrated if required.</para>
+
+ <para><emphasis>Apache Isis</emphasis> is licensed under <ulink
url="http://www.apache.org/licenses/LICENSE-2.0.html">Apache Software
- License v2</ulink>. Naked Objects is also hosted on SourceForge, and is
- also licensed under Apache Software License v2.</para>
+ License v2</ulink>.</para>
</preface>
<!-- main content -->
@@ -60,7 +65,7 @@
<title>Introduction</title>
<sect1>
- <title>Scenario Testing (aka Agile Acceptance Testing)</title>
+ <title>Behaviour-driven Development</title>
<para>Prior to agile development, requirements gathering for systems was
traditionally performed by business analysts discussing requirements
@@ -72,7 +77,7 @@
out-of-date) requirements documentation and (as often as not)
reverse-engineering the implementation.</para>
- <para>Scenario testing combines the requirements capture and the
+ <para>Behaviour-driven development combines requirements capture and the
acceptance test criteria in a single form, through scenarios. As before,
these requirements are in a form that a non-technical domain expert from
the business can understand. What differs though is that these scenarios
@@ -88,504 +93,200 @@
non-technical businesss person. Compare this to unit testing which
exercises the behaviour / method of a single class.</para>
- <para>Scenario testing is more usually called "agile acceptance
- testing", but I find that term rather clumsy. Other names also exist,
- including behaviour-driven development, and example-driven development.
- Here we use our own term "scenario testing".</para>
- </sect1>
-
- <sect1>
- <title>Introduction to FitNesse</title>
-
- <para><ulink url="http://fitnesse.org">FitNesse</ulink> is an framework
- to enable scenario testing. It builds upon the earlier <ulink
- url="http://fit.c2.com/">FIT</ulink> framework, which executes tests
- expressed in terms of tables. FitNesse sits on top of FIT<footnote>
- <para>In fact, FitNesse as of end 2008 has a replacement test
- execution architecture, called <ulink
- url="http://fitnesse.org/FitNesse.UserGuide.SliM">SLIM</ulink>.</para>
- </footnote>, allowing the tables to be written within a Wiki, and
- spawning off FIT to run the tests. The results of the tests are shown as
- annotated tests, as shown below.</para>
-
- <screenshot>
- <screeninfo>The ClaimsAppSuite test results</screeninfo>
-
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/ClaimsAppSuite-TestResults.png"
- scale="37" />
- </imageobject>
- </mediaobject>
- </screenshot>
-
- <para>This makes it easy for non-technical business users to both author
- new tests, and to view their execution. It also creates an efficient
- feedback loop; a FitNesse test will "keep on going" even if it hits a
- failure. Thus the developer can identify several issues and fix them in
- a single pass.</para>
-
- <para>Another way to think of FitNesse is as a replacement presentation
- layer, hitting the underlying domain model in the same way that the
- regular UI would.</para>
-
- <para>If using FitNesse on a "regular" project then the developer writes
- glue code that in effect take the values out of the wiki page, and use
- them to interact with the system. They then return a success/failure
- response which FitNesse then uses to annotate the results page. FitNesse
- calls this glue code a "fixture". There is some overlap with Naked
- Objects' own use of that term. However, whereas a Naked Objects fixture
- is only used to setup the initial state of a test, a FitNesse fixture
- not only does that but it also is used to execute the test
- proper.</para>
-
- <para>It is relatively straightforward to integrate FitNesse into a
- continuous integration environment; see <xref
- linkend="sec.SettingUpContinuousIntegration" /> for further
- details.</para>
+ <para>Scenario testing is closely related to (and sometimes a synonym
+ for) "agile acceptance testing"; I must admit though that I find that
+ term rather clumsy.</para>
</sect1>
<sect1>
- <title>How Tested Objects' Integration Works</title>
-
- <para>Although you could test a Naked Objects application using vanilla
- FitNesse, this would entail you having to write all the FitNesse
- fixtures to interact with the domain objects. You would also need to
- come up with a representation for the tables.</para>
-
- <para>Tested Objects' FitNesse integration is designed to let you use
- FitNesse to test your Naked Objects application without all this hassle.
- We can use the same Naked Objects metamodel that is used for the
- auto-generation of its generic OOUIs to create a set of generic fixtures
- that interact with the domain object similarly. Using Tested Objects is
- therefore just a matter of using these fixtures out-of-the-box.</para>
-
- <para>There are many such fixtures, but the one you will use the most
- often is the UsingNakedObjectsViewer fixture (see <xref
- linkend="sec.UsingNakedObjectsViewer" />). The screenshot below shows it
- in use for checking assertions at the end of a test (the "Then").</para>
-
- <screenshot>
- <screeninfo>Before</screeninfo>
-
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/UsingNakedObjectsViewerBefore.png"
- scale="40" />
- </imageobject>
- </mediaobject>
- </screenshot>
-
- <para>To explain this: the "tomsClaim1" is an alias to an object that
- has been created or obtained previously. The "perform" column lists the
- verb to do, in this case all checks. Other things that can be done
- include invoke actions, set properties and so forth. The "on member"
- column specifies the property, collection or action of the domain object
- being interacted with.</para>
-
- <para>The following screenshot shows how the test is annotated after a
- successful run:</para>
-
- <screenshot>
- <screeninfo>After</screeninfo>
-
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/UsingNakedObjectsViewerAfter.png"
- scale="40" />
- </imageobject>
- </mediaobject>
- </screenshot>
-
- <para>The end-user should be able to follow this test and could, if they
- want, run through the same set of steps manually themselves. Or, of
- course, they could just look at the test results.</para>
- </sect1>
- </chapter>
-
- <chapter id="chp.UsingTheFitNesseArchetype">
- <title>Using the FitNesse Archetype</title>
-
- <para>Like Naked Objects, Tested Objects provides a Maven archetype to get
- you up and running with quickly. Tested Objects' FitNesse archetype runs
- against any Naked Objects application, and provides a new
- <filename>xxx-fitnesse</filename> project (where <literal>xxx</literal> is
- the root artifact Id). This project bundles FitNesse, contains the
- FitNesse wiki pages that contain the tests (and also a user guide), and
- allows the FitNesse wiki server to be run.</para>
-
- <para>Out-of-the-box the archetype will generate a set of tests for the
- example claims application in the
- <filename>nakedobjects-4.0.x-for-maven.zip</filename> download<footnote>
- <para>See <ulink
- url="http://sourceforge.net/projects/nakedobjects">http://sourceforge.net/projects/nakedobjects</ulink></para>
- </footnote>, under <filename>examples/claims</filename>). These are
- great to try out <emphasis>Tested Objects</emphasis> for the first time,
- and they also provide some ideas for you to structure your own test
- suite.</para>
-
- <para>The <filename>xxx-fitnesse</filename> project generated by the
- archetype is intended to be included as a module of the parent pom
- (<filename>xxx/pom.xml</filename>) --- in Maven parlance, it is a partial
- archetype. Providing that your run the archetype in the correct directory,
- Maven will automatically include the newly generated project in the parent
- project.</para>
-
- <sect1>
- <title>Prerequisites</title>
-
- <para>To use the archetype you'll need to install <ulink
- url="http://maven.apache.org">Maven</ulink> (and there's a good chance
- you've done this already if you're using the Naked Objects Maven
- archetype).</para>
-
- <para>Optionally (and recommended) you can install the <ulink
- url="http://m2eclipse.sonatype.org/">m2eclipse</ulink> plugin for <ulink
- url="http://eclipse.org">Eclipse</ulink> IDE (again, there's a good
- chance you've done this too).</para>
- </sect1>
-
- <sect1>
- <title>Running the Archetype</title>
-
- <para>You can either run the archetype from the command line (and then
- import into Eclipse if using m2eclipse), or run the archetype using
- m2eclipse straight off.</para>
+ <title>Concordion Integration</title>
<sect2>
- <title>Running the archetype from the command line</title>
-
- <para>Navigate to the parent directory of the project (that holds the
- parent pom.xml). The directions here are for the examples/claims
- project.</para>
-
- <remark>TODO: Naked Objects 4.0.0 released examples/claims as
- org.nakedobjects.distribution:examples-claims. In 4.0.1 (the plan is)
- for it to be released as org.nakedobjects.examples:claims. In the
- screenshots that follow I've hacked the 4.0.0 pom to change the
- groupId and artifactId. However, I've provided the command that kicks
- the whole thing for both 4.0.0 and 4.0.1</remark>
-
- <para>To run the archetype from the command line (against NOF
- 4.0.0):</para>
-
- <screen>$ cd $NO_HOME/examples-claims
-$ mvn archetype:generate \
- -D archetypeCatalog=http://starobjects.sourceforge.net/m2-repo/snapshot \
- -D archetypeGroupId=org.starobjects.tested.fitnesse \
- -D archetypeArtifactId=archetype \
- -D archetypeVersion=1.0-beta-3-SNAPSHOT \
- -D groupId=org.nakedobjects.distribution \
- -D artifactId=claims-fitnesse \
- -D parentArtifactId=claims \
- -D package=org.nakedobjects.examples.claims.fitnesse \
- -D version=4.0.0</screen>
-
- <para>or against NOF 4.0.1:</para>
-
- <screen>$ cd $NO_HOME/examples-claims
-$ mvn archetype:generate \
- -D archetypeCatalog=http://starobjects.sourceforge.net/m2-repo/snapshot \
- -D archetypeGroupId=org.starobjects.tested.fitnesse \
- -D archetypeArtifactId=archetype \
- -D archetypeVersion=1.0-beta-3-SNAPSHOT \
- -D groupId=org.nakedobjects.examples \
- -D artifactId=claims-fitnesse \
- -D parentArtifactId=claims \
- -D package=org.nakedobjects.examples.claims.fitnesse \
- -D version=4.0.1</screen>
-
- <para>The groupId:parentArtifactId:version tuple should correspond to
- the parent pom (so, for 4.0.0, that is
- <classname>org.nakedobjects.distribution:claims:4.0.0</classname>, and
- for 4.0.1 it is
- <classname>org.nakedobjects.examples:claims:4.0.1</classname>). Note
- that the parentArtifactId is not one of the standard Maven properties,
- instead it is an additional property mandates by the Tested Objects'
- archetype. (The archetype uses this property to derive the names of
- some the other projects to include in the dependencies).</para>
-
- <para>The artifactId then is the artifact of the FitNesse project
- being generated, so is typically
- <emphasis>parentname</emphasis>-fitnesse (so here:
- <classname>claims-fitnesse</classname>).</para>
-
- <para>The output is shown below:</para>
-
- <para><screenshot>
- <screeninfo>Generating the Maven Archetype from the command
- line</screeninfo>
-
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/RunningMavenArchetypeFromCommandLine.png"
- scale="40" />
- </imageobject>
- </mediaobject>
- </screenshot></para>
-
- <para>This will generate a claims-fitnesse directory, and update the
- parent pom.xml file.</para>
-
- <para>We can then import into project into Eclipse, using File >
- Import > Maven</para>
-
- <screenshot>
- <screeninfo>Importing the generated Maven Archetype into Eclipse (1
- of 2)</screeninfo>
-
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/ImportingMavenProject.png" scale="40" />
- </imageobject>
- </mediaobject>
- </screenshot>
+ <title>Introduction to Concordion</title>
- <screenshot>
- <screeninfo>Importing the generated Maven Archetype into Eclipse (2
- of 2)</screeninfo>
-
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/ImportingMavenProject2.png"
- scale="40" />
- </imageobject>
- </mediaobject>
- </screenshot>
+ <para><ulink url="http://concordion.org">Concordion</ulink> is a
+ framework to enable scenario testing. It is implemented as a <ulink
+ url="http://junit.org">JUnit4</ulink> test runner, with the test form
+ being written in <acronym>XHTML</acronym>. The domain expert /
+ business analyst authors new stories using an <acronym>XML</acronym>
+ editor (<ulink url="http://xmlmind.net">XmlMind</ulink> is one
+ commercial editor that we recommend); once executed as tests, the
+ results are shown as the same XHTML document, annotated to indicate
+ which assertions have succeeded, and which have failed. It also
+ creates an efficient feedback loop; a Concordion test will "keep on
+ going" even if it hits a failure. Thus the developer can identify
+ several issues and fix them in a single pass.</para>
+
+ <para>Another way to think of Concordion is as a replacement
+ presentation layer, hitting the underlying domain model in the same
+ way that the regular <acronym>UI</acronym> would. (This is why we call
+ this module is called the <acronym>BDD
+ </acronym><emphasis>viewer</emphasis>).</para>
+
+ <para><acronym>Concordion</acronym> works using a "convention over
+ configuration" approach, matching the <acronym>XHTML</acronym> text
+ file with a corresponding JUnit4 test run set up to run using
+ Concordion's <classname>ConcordionRunner</classname>, The developer
+ then annotates the <acronym>XHTML</acronym> using special
+ (Concordion-namespaced) attributes in order identify the inputs to and
+ expected results of the test. This is used by the
+ <classname>ConcordionRunner</classname> to call into corresponding
+ methods in the test.</para>
+
+ <para>For example, suppose the analyst writes a scenario test called
+ <filename>CustomerPlacesOrder.xhtml</filename>. In the
+ <acronym>XHTML</acronym> the analyst has identified the details of the
+ customer doing the ordering (customer ref 4321, say), the product
+ being ordered (product code 1234), the fact that the customer
+ initially has no orders, and that the customer has no invoices
+ outstanding. The test concludes with an assertion that there is now an
+ unfulfilled order for the customer, and that the customer now has an
+ invoice to be paid.</para>
+
+ <para>The developer in turn edits the <acronym>XHTML</acronym>,
+ identifying the customer and the product. He then further edits the
+ <acronym>XHTML</acronym> to call a method in the JUnit4 test
+ representing the placing of an order:
+ <methodname>placeOrder()</methodname>, say. And he finishes by
+ annotating the <acronym>XHTML</acronym> to make assertions about the
+ post conditions (unfulfilled order, new invoice to be paid
+ etc).</para>
+
+ <para>Then, the developer writes a JUnit4 test alongside the
+ <acronym>XHTML</acronym>; in this example it would be called
+ <filename>CustomerPlacesOrderTest.java</filename>. Concordion calls
+ into this JUnit4 test as it comes across the annotations in the
+ <acronym>XHTML</acronym>, and the JUnit4 test mediates with the system
+ under test.</para>
+
+ <para>When the test runs, Concordion generates a copy of the
+ <acronym>XHTML</acronym> in a output directory (by default specified
+ by a system property) which can then made available for inspection by
+ the business analyst (eg published on a website).</para>
+
+ <para>The Concordion website has a good <ulink
+ url="http://concordion.org/Tutorial.html">tutorial</ulink> that
+ demonstrates all the above, and can be completed in 20~30
+ minutes.</para>
</sect2>
<sect2>
- <title>Running the archetype from m2eclipse</title>
-
- <para>As an alternative to running the archetype from the command
- line, you can instead run the archetype using m2eclipse.</para>
-
- <para>However, m2eclipse does not (seem to) understand partial
- archetypes, and so the project cannot be generated in a subfolder of
- the parent project. Instead, I recommend you create the project
- alongside the parent (eg in
- <filename>examples/claims-fitnesse</filename>).</para>
-
- <remark>TODO: These notes are slightly incomplete; it is also
- necessary to specify the archetype catalog in the
- Windows>Preferences. However, m2eclipse 0.9.8 (the latest 'stable'
- version at the time of writing) fails to pick up snapshot archetypes
- from remote catalogs. The workaround is either to use the command line
- once (this will copy the archetype into your local repository) or
- alternatively achieve the same thing by building the FitNesse code
- from scratch, as described in the developers' guide.</remark>
-
- <para>So, use File > New > Project, then Maven > Maven
- Project to bring up the wizard:</para>
-
- <screenshot>
- <screeninfo>Generating the Maven Archetype using m2eclipse (1 of
- 4)</screeninfo>
-
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/m2eclipse.GenerateProject1.png"
- scale="40" />
- </imageobject>
- </mediaobject>
- </screenshot>
-
- <para>Specify the directory. Note that this cannot be a directory that
- contains a pom.xml file.</para>
-
- <screenshot>
- <screeninfo>Generating the Maven Archetype using m2eclipse (2 of
- 4)</screeninfo>
-
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/m2eclipse.GenerateProject2.png"
- scale="40" />
- </imageobject>
- </mediaobject>
- </screenshot>
-
- <para>Select the archetype. (Note that the screenshot here shows the
- archetype in the "Default Local" catalog ... that's because I've built
- the archetype from source. ).</para>
+ <title>How the Isis/Concordion Integration Works</title>
- <screenshot>
- <screeninfo>Generating the Maven Archetype using m2eclipse (3 of
- 4)</screeninfo>
+ <para>Although you could test an Apache Isis application using vanilla
+ Concordion, this would entail you having to write all the glue code
+ yourself to interact with the domain objects. You would also need to
+ encode the rules that are normally implemented by the viewer, eg so
+ that a hidden action cannot be invoked, and an invalid value for a
+ property cannot be set.</para>
+
+ <para>The <emphasis>BDD viewer</emphasis> integration provided by
+ <emphasis>Apache Isis</emphasis> works by providing a superclass for
+ the JUnit4 test, called
+ <classname>AbstractIsisConcordionTest</classname>. This does several
+ things:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>it bootstraps an instance of <emphasis>Apache
+ Isis</emphasis> system using the in-memory object store;</para>
+ </listitem>
+
+ <listitem>
+ <para>the system is initialized with a set of services, picked up
+ from the <filename>isis.properties</filename> configuration
+ file</para>
+ </listitem>
+
+ <listitem>
+ <para>it provides methods to allow fixtures (domain objects) to be
+ installed into the object store</para>
+ </listitem>
+
+ <listitem>
+ <para>it provides methods to allow a user to be logged in, and the
+ date to be specified</para>
+ </listitem>
+
+ <listitem>
+ <para>it provides methods to allow the user to interact with
+ services and domain objects:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>asserting on the value of properties and the contents of
+ collections</para>
+ </listitem>
+
+ <listitem>
+ <para>setting the value of a property (if valid) and adding
+ to/removing from a collection (if valid)</para>
+ </listitem>
+
+ <listitem>
+ <para>invoking actions</para>
+ </listitem>
+
+ <listitem>
+ <para>asserting on the state of a class member (hidden,
+ disabled or enabled)</para>
+ </listitem>
+ </itemizedlist>
+ </listitem>
+ </itemizedlist>
+
+ <para>For each <acronym>XHTML</acronym> scenario test, the developer
+ writes subclasses the AbstractIsisConcordionTest, creating a name
+ matching the scenario test (ie as per regular Concordion). He
+ then</para>
+
+ <para>annotates the original <acronym>XHTML</acronym>, either calling
+ directly into the inherited methods, or writing small simple methods
+ to delegate to these inherited methods as required. The Concordion
+ website has some <ulink
+ url="http://concordion.org/Technique.html">hints and tips</ulink> to
+ help you find the right balance between these two approaches.</para>
+ </sect2>
+ </sect1>
+
+ <sect1>
+ <title>Common Library</title>
+
+ <para>The Concordion integration is based on a library that factors out
+ interactions with the running Isis system. This library is intended to
+ make it easy to support other BDD frameworks if required.<footnote>
+ <para>In fact, the first BDD framework integration was to <ulink
+ url="http://fitnesse.org">FitNesse</ulink>. This has now been
+ dropped because FitNesse has an incompatible license with Apache
+ Software Foundation. The original FitNesse code can still be found
+ on the <ulink
+ url="http://isis-contrib.sourceforge.net">Isis-contrib</ulink>
+ companion site, and does indeed use the common library.</para>
+ </footnote></para>
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/m2eclipse.GenerateProject3.png"
- scale="40" />
- </imageobject>
- </mediaobject>
- </screenshot>
-
- <para>Specify the groupId, artifactId (eg
- <classname>claims-fitnesse</classname>), version and package. Also
- specify the parentArtifactId as the parent project's artifactId (eg
- <classname>claims</classname>):</para>
-
- <screenshot>
- <screeninfo>Generating the Maven Archetype using m2eclipse (4 of
- 4)</screeninfo>
-
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/m2eclipse.GenerateProject4.png"
- scale="40" />
- </imageobject>
- </mediaobject>
- </screenshot>
-
- <para>Hit Finish and you will be in more-or-less the same place as if
- you had generated the archetype from the command line and then
- imported. The only thing different is that the new fitnesse project
- will not be in the parent project's modules.</para>
-
- <para>Therefore, navigate to the parent project's
- <sgmltag><modules></sgmltag> region, and add:</para>
-
- <para><screen><modules>
- <module>dom</module>
- <module>fixture</module>
- <module>service</module>
- <module>commandline</module>
- <module>webapp</module>
- <module>../claims-fitnesse</module>
-</modules></screen>The instructions in the wiki <emphasis>do</emphasis>
- assume that you have done this.</para>
- </sect2>
- </sect1>
+ <para>The main concepts that the common library exposes are:</para>
- <sect1>
- <title>Starting FitNesse Wiki</title>
+ <itemizedlist>
+ <listitem>
+ <para>AliasItemsInListPeer</para>
+ </listitem>
- <para>Once the fitnesse project has been imported you can start the
- wiki. Navigate to <filename>ide/eclipse/launch</filename> and there will
- be a launch configuration:</para>
-
- <screenshot>
- <screeninfo>Launch Configuratoin for the FitNesse wiki</screeninfo>
-
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/FitNesseWikiLaunchConfiguration.png"
- scale="40" />
- </imageobject>
- </mediaobject>
- </screenshot>
-
- <para>Start by right clicking and selecting run as. The FitNesse wiki
- should start on port 9090:</para>
-
- <screenshot>
- <screeninfo>FitNesse Wiki running in the Eclipse console</screeninfo>
-
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/Console.FitNesseWikiServer.png"
- scale="40" />
- </imageobject>
- </mediaobject>
- </screenshot>
- </sect1>
+ <listitem>
+ <para>AliasServicesPeer</para>
+ </listitem>
- <sect1>
- <title>Setting up to Run Tests</title>
+ <listitem>
+ <para>CheckListPeer</para>
+ </listitem>
- <para>Using your favorite browser, navigate to <ulink
- url="http://localhost:9090/FrontPage">http://localhost:9090/FrontPage</ulink>;
- you should see the introductory page:</para>
-
- <screenshot>
- <screeninfo>The FitNesse Wiki FrontPage</screeninfo>
-
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/FitNesseWikiFrontPage.png" scale="40" />
- </imageobject>
- </mediaobject>
- </screenshot>
-
- <para>Before we can run the generated tests there are a couple of things
- we need to take care of (both are documented on FrontPage). First, we
- need to copy the application into a location so that FitNesse can find
- it. This is done by simply running mvn clean install on the
- project:</para>
-
- <screenshot>
- <screeninfo>Running mvn clean install</screeninfo>
-
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/RunningMvnCleanInstall.png" scale="40" />
- </imageobject>
- </mediaobject>
- </screenshot>
-
- <para>(Note, if for any reason you didn't include the xxx-fitnesse
- project into the parent project, you'll need to run mvn clean install
- for the xxx-fitnesse project as well).</para>
-
- <para>Secondly, the pages generated by the archetype use the
- <filename>nakedobjects.properties</filename> to bootstrap Naked Objects.
- By default, this is assumed to be in
- <filename>../commandline/config/nakedobjects.properties</filename>. If
- this isn't the case, then edit the BootStrapNakedObjects page as
- required:</para>
-
- <screenshot>
- <screeninfo>Edit BootstrapNakedObjects if required</screeninfo>
-
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/EditBootstrapNakedObjects.png"
- scale="40" />
- </imageobject>
- </mediaobject>
- </screenshot>
- </sect1>
+ <listitem>
+ <para></para>
+ </listitem>
+ </itemizedlist>
- <sect1>
- <title>Running the Tests</title>
+ <para></para>
- <para>If you are trying out the archetype against the examples/claims
- application then you'll probably want to run the existing tests to check
- everything works.</para>
-
- <para>The tests are organized into a single application suite,
- ClaimsAppSuite. This in turn defines two subsuites:</para>
-
- <screenshot>
- <screeninfo>The ClaimsAppSuite</screeninfo>
-
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/ClaimsAppSuite.png" scale="35" />
- </imageobject>
- </mediaobject>
- </screenshot>
-
- <para>FitNesse will automatically invoke all tests in the hierarchy,
- therefore just press the Suite button on the left:</para>
-
- <screenshot>
- <screeninfo>The ClaimsAppSuite test results</screeninfo>
-
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/ClaimsAppSuite-TestResults.png"
- scale="35" />
- </imageobject>
- </mediaobject>
- </screenshot>
-
- <para>If you've run the archetype against your own project though, then
- you can either delete these ClaimsAppSuites, or leave them around for
- reference. To ensure that they don't accidentally run, use the
- Properties button (on the left hand side) and deselect the Suite and
- Test properties.</para>
+ <para></para>
</sect1>
</chapter>
@@ -815,7 +516,13 @@ $ mvn archetype:generate \
page.</para>
<sect2 id="sec.StoryFixture">
- <title>StoryFixture</title>
+ <title>Story</title>
+
+ <para>Concordion: AbstractIsisConcordionTest</para>
+
+ <para>FitNesse: StoryFixture</para>
+
+ <para></para>
<para>Sets up the workflow story test. Boilerplate, should always be
the first FitNesse fixture included in a page.</para>
@@ -834,6 +541,14 @@ $ mvn archetype:generate \
<sect2 id="sec.SetConfigDirectory">
<title>SetConfigDirectory</title>
+ <para>Concordion:
+ AbstractIsisConcordionTest#bootstrapIsis(configDirectory,
+ deploymentType)</para>
+
+ <para>FitNesse: StoryFixture#setConfigDirectory</para>
+
+ <para></para>
+
<para>Specifies the config directory containing
<filename>nakedobjects.properties</filename>. Called after
StoryFixture (see <xref linkend="sec.StoryFixture" />), and before
@@ -856,6 +571,14 @@ $ mvn archetype:generate \
<sect2 id="sec.EnableExploration">
<title>EnableExploration</title>
+ <para>Concordion:
+ AbstractIsisConcordionTest#bootstrapIsis(configDirectory,
+ deploymentType) ... derived from deploymentType</para>
+
+ <para>FitNesse: StoryFixture#enableExploration()</para>
+
+ <para></para>
+
<para>Enables exploration actions if required. Should be called before
InitNakedObjects (see <xref linkend="sec.InitNakedObjects" />).</para>
@@ -876,7 +599,15 @@ $ mvn archetype:generate \
</sect2>
<sect2 id="sec.InitNakedObjects">
- <title>InitNakedObjects</title>
+ <title>InitIsis</title>
+
+ <para>Concordion:
+ AbstractIsisConcordionTest#bootstrapIsis(configDirectory,
+ deploymentType) ... derived from deploymentType</para>
+
+ <para>FitNesse: StoryFixture#initIsis()</para>
+
+ <para></para>
<para>Initializes the Naked Objects runtime using the services
specified through the UseConfigDirectory fixture (see <xref