Hi,
We’ve got the same issue, and have an approach that is in the early stages of development. I’m afraid I can’t share the complete code at the current time - still a fair bit of work to do pulling it together into a reusable package; it’s currently in a proof-of-concept project.
We’ve split the problem into separate two parts : creating a debugId for a component, and assigning it.
Creating a DebugId
We’ve come up with the idea of a Naming strategy, that generates a name for a given component :
public interface NamingStrategy {
/**
* Returns the name for a given component, or null if one cannot be derived.
*
* @param component
* @return
*/
public String getName(Component component);
}
We’ve currently got four main implementations :[list]
[]
CaptionNamingStrategy : Returns the components caption
[]
HasNameNamingStrategy : If the component implements a HasName interface, use that. This mainly is so that we can support I18N, and return the resourceBundle key for the component as the name.
[]
CompositeNamingStrategy : Given a number of naming stragies, returns the result of the first strategy that returns a non-null result. This allows to combine HasName and Caption strategies into one single strategy.
[]
CompoundNamingStrategy : Given a single strategy, generates names for all the components going up the component tree, joining them with “.” (and ignoring null results) : e.g. AddressWindow.AddressForm.addressLine1
[/list]
Assigning the DebugId
Instead of assigning the debugId at component creation time, we need to assign it when the component is attached to the application (i.e. added into the window), so that we can be sure that the component has a parent. As far as we are aware, components almost always get added to a ComponentContainer; we have a (badly named) TestBenchIdAssigner that implements ComponentContainer.ComponentAttached and ComponentContainer.ComponentDetached, and add it to the layout of the window. Every time a component is attached it [list=1]
[]
If the component does NOT have a DebugId, assigns the debug id, based on a naming strategy - typically a CompoundNamingStrategy(CompositeNameStategy(HasNameNamingStrategy, CaptionNamingStrategy))
[]
If the component is a ComponentContainer, it [list]
[]
adds itself as a ComponentContainer.ComponentAttached, ComponentContainer.ComponentDetached lisetner, which allows it to get propogated down the component tree.
[]
recursively iterates around the children of the component container, generating debugId’s and adding itself as listener to child ComponentContainers.
[/list]
[/list]
When the component is detached, if it is a ComponentContainer [list]
[]
it removes itself as an Attach/Detach listener.
[]
recursively iterates around the children of the component container, removing itself as listener to child ComponentContainers.
[/list]
There are some edge cases here :
Form may need to be handled carefully : from memory, it does not implement ComponentContainer, so the component iteration in TestBenchIdAssigner may needs to be a bit smart and iterate the form’s layout. (or, you could use a subclass of Form and make that implement ComponentContainer)
If you remove a component, and add it elsewhere, the first debugId is currently retained - which could result in a naming clash. We’re not currently removing the DebugId in the detach method, because it might have been manually assigned. Our proposal would be to prefix the automatically-assigned debugId, so that we can identifiy as such at detach time, and removed.
Conclusion
This possibly over-engineered solution, assuming that all important components have a static caption/name and that you don’t have two components with the same name inside the same container, should give you unique debugIds within a window, allowing testing tools to uniquely identify components.
Apologies for the lengthy response. I hope it makes some sort of sense - any questions, please feel free to ask. Our intention is that - when it is in a more usable state, and when we have enough time - we will contribute the code to the Vaadin AddOns Directory; I currently have no timeframe for that. In the mean time, hopefully this post gives you an outline of one approach.
Cheers,
Charles.