That's fine with me. This week I'm starting to look at automated web browser tests (possibly using Geb [1] as a layer over Selenium). Plan is for Estatio to act as a regression suite for Isis as a whole. So no immediate rush my end.
Cheers Dan [1] http://www.gebish.org/ On 8 September 2013 08:35, GESCONSULTOR <[email protected]> wrote: > For sure! > > Unless you or Jeremy plan to use it immediately let me check and improve > it this week through some BDD features we must implement. > > Thanks, > > Oscar > > > El 08/09/2013, a las 09:20, Dan Haywood <[email protected]> > escribió: > > > Looks very good, Oscar, exactly the sort of thing that should be in the > > framework, I think. > > > > Could you raise a ticket and provide a commit? If you have the time, > > perhaps you could also refactor the ToDo app to show its use? > > > > Many thanks > > > > Cheers > > Dan > > > > > > > > On 7 September 2013 19:05, GESCONSULTOR - Óscar Bou > > <[email protected]>wrote: > > > >> > >> I've just been thinking about a way to avoid to define a Spec > Transformer > >> for every Entity class in the Domain. > >> > >> As it's just "infrastructure software", I thought that was "just > enough". > >> > >> The attached code allows to use just one Spec Transformer when the Glue > is > >> written like this: > >> - the employee with name "PETER" > >> - the employee named "PETER" > >> - the property with reference "REF-001" > >> - the property referenced "REF-001" > >> - the product with id "PR-001" > >> > >> From there, we know that: > >> - The Entity singular name is "employee" (so it can be obtained from the > >> Isis Object Specifications, reinforcing the Ubiquitous Language use on > BDD > >> specifications). > >> - We must search by name > >> - The name must be PETER > >> > >> > >> > >> For using it, I must define a Glue like this one: > >> > >> Define a Glue like this one: > >> @When("The company (employee with name \"[^\"]*\") has a role assigned") > >> public void > >> > the_company_employee_with_name(@Transform(GenericIsisJdoTransformer.class) > >> final Employee employee) { > >> ... > >> } > >> > >> > >> > >> First "proof-of-concept" code version follows this text. > >> > >> It can be improved in many ways, for allowing customization through > >> inheritance, avoiding JDO through the use of the Apache Isis query > methods, > >> etc. > >> > >> > >> But I would let you know, right to know if you think it can be useful. > >> > >> On this way, the only implemented classes are the ones supporting the > >> Glues (and only the Spec Transformers for more specific use cases). > >> > >> > >> HTH, > >> > >> Oscar > >> > >> > >> > >> > >> > >> --------------------- > >> > >> > >> > >> > >> package com.xms.framework.testing.integration.spectransformers; > >> > >> import java.util.HashMap; > >> import java.util.Map; > >> import java.util.regex.Matcher; > >> import java.util.regex.Pattern; > >> > >> import javax.jdo.Query; > >> > >> import org.opensaml.artifact.InvalidArgumentException; > >> > >> import org.apache.isis.applib.DomainObjectContainer; > >> import org.apache.isis.core.metamodel.spec.ObjectSpecification; > >> import org.apache.isis.core.runtime.system.context.IsisContext; > >> import org.apache.isis.core.specsupport.scenarios.ScenarioExecution; > >> import > >> > org.apache.isis.objectstore.jdo.datanucleus.service.support.IsisJdoSupportImpl; > >> > >> /** > >> * Requires the Gherkin's capture in the format '([entitySingularName] > >> [(.+?) > >> * name(d)|(.+?) reference|(.+?) id] \"[entityId]\")'. > >> * <p> > >> * For example: > >> * <ul> > >> * <li>the employee with name "PETER"</li> > >> * <li>the employee named "PETER"</li> > >> * <li>the property with reference "REF-001"</li> > >> * <li>the property referenced "REF-001"</li> > >> * <li>the product with id "PR-001"</li> > >> * </ul> > >> * <p> > >> * For matching the first one we will need the following Gherkin regular > >> * expression: > >> * <ul> > >> * <li> > >> * \\@When("The company's (employee with name \"[^\"]*\") has a role > >> assigned")</li> > >> * </ul> > >> * <p> > >> * From there, we know that: > >> * <ul> > >> * <li>The Entity singular name is "employee".</li> > >> * <li>We must search by name</li> > >> * <li>The name must be PETER</li> > >> * </ul> > >> * > >> */ > >> public class GenericIsisJdoTransformer extends > >> NullRecognizingTransformer<Object> { > >> > >> private static Map<String, ObjectSpecification> > >> specificationsBySingularName; > >> > >> /** > >> * Tries to obtain the Entity class name, id and search field from > the > >> * Cucumber capture, and find it on the JDO Object Store. > >> * > >> * @see com.xms.framework.testing.integration.spectransformers. > >> * NullRecognizingTransformer#transformNonNull(java.lang.String) > >> */ > >> @Override > >> protected Object transformNonNull(final String capture) { > >> > >> return this.findFromCapture(capture); > >> > >> } > >> > >> /** > >> * @param entityName > >> * Name of the Entity specified on the Gherkin's capture. > >> * @return > >> */ > >> private ObjectSpecification specificationOf(final String entityName) > { > >> > >> if (IsisJdoTransformer.specificationsBySingularName == null) { > >> IsisJdoTransformer.specificationsBySingularName = new > >> HashMap<String, ObjectSpecification>(); > >> > >> for (final ObjectSpecification current : > >> IsisContext.getSpecificationLoader().allSpecifications()) { > >> > >> > >> > IsisJdoTransformer.specificationsBySingularName.put(current.getSingularName().toLowerCase(), > >> current); > >> } > >> > >> } > >> return > >> > IsisJdoTransformer.specificationsBySingularName.get(entityName.toLowerCase()); > >> > >> } > >> > >> private final Class<?> entityClassFrom(final String capture) { > >> > >> // The Entity Id will be between "". > >> String entityName = ""; > >> > >> final String[] recognizedPatterns = { "( with name )", "( named > >> )", "( with reference )", "( referenced )", "( with id )" }; > >> > >> for (final String currentPattern : recognizedPatterns) { > >> final Pattern p = Pattern.compile(currentPattern); > >> final Matcher m = p.matcher(capture); > >> if (m.find()) { > >> entityName = capture.substring(0, m.start()); > >> break; > >> } > >> } > >> > >> if (entityName == "") { > >> throw new InvalidArgumentException(String.format("Cannot find > >> the entity's name on the capture %s. The format must be > >> '([entitySingularName] [with name|with reference|named|referenced] > >> \"[entityId]\")'", capture)); > >> } > >> > >> final ObjectSpecification specification = > >> this.specificationOf(entityName); > >> if (specification == null) { > >> throw new InvalidArgumentException(String.format("There is no > >> Entity registered in Isis with '%s' as it's singular name", > entityName)); > >> } > >> return specification.getCorrespondingClass(); > >> > >> } > >> > >> /** > >> * @param capture > >> * The Gherkin's capture > >> * @return > >> */ > >> private final String filterFrom(final String capture) { > >> // Find by "name". > >> if (capture.matches("(.+?)name(.+?)")) { > >> return String.format("name==\"%s\"", > >> this.entityIdFrom(capture)); > >> } > >> > >> // Find by "reference". > >> if (capture.matches("(.+?)reference(.+?)")) { > >> return String.format("reference==\"%s\"", > >> this.entityIdFrom(capture)); > >> } > >> > >> // Find by "id". > >> if (capture.matches("(.+?)id(.+?)")) { > >> return String.format("id==\"%s\"", > this.entityIdFrom(capture)); > >> } > >> > >> throw new InvalidArgumentException(String.format("The entity id > >> has not been found on the capture '%s'. It must be between two \" > >> characters.", capture)); > >> } > >> > >> private final Object entityIdFrom(final String capture) { > >> // The Entity Id will be between "". > >> final Pattern p = Pattern.compile("\"(.+?)\""); > >> final Matcher m = p.matcher(capture); > >> if (m.find()) { > >> return m.group().replace("\"", ""); > >> } else { > >> throw new InvalidArgumentException(String.format("The entity > >> id has not been found on the capture '%s'. It must be between two \" > >> characters.", capture)); > >> } > >> } > >> > >> private final Object findFromCapture(final String capture) { > >> > >> // Need to flush(). > >> // The Entity can be created on the same transaction. > >> // If we don't flush() the changes, perhaps the entity have not > >> been > >> // saved to the object store yet, and will not be found. > >> > >> > ScenarioExecution.current().service(DomainObjectContainer.class).flush(); > >> > >> final Query query = > >> > ScenarioExecution.current().service(IsisJdoSupportImpl.class).getJdoPersistenceManager().newQuery(); > >> query.setClass(this.entityClassFrom(capture)); > >> query.setFilter(this.filterFrom(capture)); > >> query.setUnique(true); > >> > >> final Object result = query.execute(); > >> > >> return result; > >> > >> } > >> } > >> > >> > >> > >> > >> --------------------- > >> > >> > >> > >> > >> > >> El 27/08/2013, a las 20:24, Kevin Meyer - KMZ <[email protected]> > escribió: > >> > >>> Hi Jeremy, > >>> > >>> If I remember correctly, Dan has examples for overriding the "Finder" > >>> methods in the ToDo demo / artefact that use JDO search classes to > >>> build queries. > >>> > >>> In fact: > >>> > >>> In the "ToDoItemsJdo" class (which extends an > >>> AbstractFactoryAndRepository): > >>> // {{ notYetComplete (action) > >>> @Override > >>> protected List<ToDoItem> doNotYetComplete() { > >>> return allMatches( > >>> new QueryDefault<ToDoItem>(ToDoItem.class, > >>> "todo_notYetComplete", > >>> "ownedBy", currentUserName())); > >>> } > >>> // }} > >>> > >>> This appears in an old copy of the repo I have in the wicket JDO > >>> quickstart project. > >>> > >>> So while you can't use "FindByPattern", JDO has implemented a > >>> useful alternative. > >>> > >>> Regards, > >>> Kevin > >>> > >>> > >>> On 27 Aug 2013 at 14:37, Jeremy Gurr wrote: > >>> > >>>> I'm playing around with the cucumber support tools in isis (a testing > >> framework for behavior driven development, for those who don't know), > and > >> have created a test that basically looks like this: > >>>> > >>>> <snip> > >>>> > >>>> It's very convenient that cucumber instantiates my ServiceClass model > >> object and automatically plugs in fields according to the feature spec > >> column header. This enables me to add new fields simply by adding a > column > >> in the spec, and adding the corresponding column in my model object. It > >> skips the extra hassle of having to update the glue code as well as > service > >> methods to construct the object. > >>>> > >>>> The "exists" method contains this code: > >>>> > >>>> public boolean exists(ServiceClass serviceClass) { > >>>> final QueryFindByPattern<ServiceClass> query = new > >> QueryFindByPattern<ServiceClass>(ServiceClass.class, serviceClass); > >>>> final ServiceClass firstMatch = firstMatch(query); > >>>> > >>>> return firstMatch != null; > >>>> } > >>>> > >>>> I'm just trying to verify that an object exists with fields matching > >> the incoming object, some fields of which may be null, meaning that they > >> can be any value. The QueryFindByPattern class seemed to be a perfect > fit > >> since I'm passing around ServiceClass instances anyway. However, > running it > >> executes code that is not yet implemented: > >>>> > >>>> <snip> > >> > >> >
