Wie in Kapitel 6 beschrieben ist QF-Test in der Lage, jenseits
der Struktur von GUI Elementen mit Unterelementen zu arbeiten, die selbst keine GUI
Elemente sind, z.B. die Zellen einer Tabelle, Knoten in einem Baum oder Zeichnungen auf
einem Canvas. Solche Unterelemente werden mit Hilfe des ItemResolver
Mechanismus implementiert, über den Sie auch Ihre eigenen spezifischen Unterelemente
erstellen können.
Das ItemResolver
Interface ist deutlich komplexer als die einfachen
NameResolver2
oder FeatureResolver2
Interfaces aus dem
vorhergehenden Abschnitt und es kann nicht ohne gute Programmierkenntnisse und
Verständnis der zu Grunde liegenden Konzepte implementiert werden. Außerdem sind
ItemResolver
und die im nächsten Abschnitt beschriebenen
Checker
eng miteinander verknüpft und sollten gemeinsam implementiert
werden, wenn Sie Ihre Unterelemente auch überprüfen können wollen.
Lassen Sie sich davon aber nicht abschrecken, denn andererseits ist dieser Mechanismus
sehr mächtig und wenn Ihre ItemResolver
erst einmal implementiert und
registriert sind, integrieren sie sich so nahtlos in QF-Test, dass kein Unterschied
zwischen standard und nicht-standard Unterelementen zu erkennen ist.
Sie finden im Verzeichnis
qftest-5.2.1/Jython/Lib
unter QF-Tests
Wurzelverzeichnis einige Beispielimplementierungen, wie ktable.py
oder gef.py
. Beide Resolver sind zwar für SWT
spezifische Tabellen, das Konzept allerdings ist bei allen Engines
das gleiche.
Bevor Sie mit der Umsetzung eines ItemResolvers
beginnen können, müssen
Sie sich über die Art der Unterelemente klar werden, die Ihr GUI Element enthalten
kann. Es könnte mehr als eine Art geben, so dass diese Entscheidung willkürlich ist.
So haben wir z.B. Unterelemente für Swing, SWT und JavaFX Tabellen so implementiert, dass
sowohl einzelne Zellen, als auch ganze Spalten als Unterelemente unterstützt werden.
Letzteres ist sehr hilfreich, weil damit Checks für komplette Spalten möglich sind.
Als nächstes müssen Sie entscheiden, wie Sie die Unterelemente intern darstellen
wollen. Sie können jede Art von Object
verwenden, da QF-Test niemals auf
Ihre interne Repräsentation zugreift, sondern diese lediglich zwischen den Methoden
Ihres ItemResolvers
hin und her reicht. Was am besten funktioniert, hängt
von der API des GUI Elements ab. Wenn dieses bereits ein Konzept für Unterelemente
bietet, ist es vermutlich am besten, diese Klassen zu verwenden.
Die wichtigste Entscheidung ist, wie Sie ein Unterelement gegenüber QF-Test
repräsentieren. Wie in Kapitel 6 beschrieben, kann der Anwender
ein Unterelement über einen numerischen oder einen textuellen Index adressieren, wobei
letzterer auch ein regulärer Ausdruck sein kann. Sie müssen also zwischen einem
Unterelement und seinem Index (oder seinen Indizes) eine bidirektionale Abbildung
ermöglichen, d.h. Sie müssen folgende Fragen beantworten können:
-
Was ist der numerische und was der textuelle Index für ein gegebenes Unterelement?
-
Welches Unterelement passt am besten zu einem gegebenen numerischen oder textuellen
Index?
Die Kernpunkte bei der Vergabe von Namen für Unterelemente sind die gleichen wie bei
normalen GUI Elementen. Arbeiten Sie hierzu bitte Abschnitt 5.6 und
Abschnitt 5.7 gründlich durch bevor Sie fortfahren.
Ein einzelner numerischer oder textueller Index wird durch ein
SubItemIndex
Objekt repräsentiert. Das aktuelle Konzept unterstützt die
Adressierung eines Unterelements mittels eines primären und eines sekundären Index. In
der Zukunft hoffen wir, Indizes mit beliebiger Tiefe behandeln zu können, so dass ein
Knoten in einem Baum statt über einen einzelnen Pfad auch über einen gemischten Index
wie "tree@Root&1%Name:.*" angesprochen werden könnte. Daher ist der gesamte Index
ein Array von SubItemIndex
Objekten, auch wenn dessen Länge aktuell auf
ein oder zwei Objekte beschränkt ist.
Die meisten Unterelemente haben eine Geometrie, also eine Position und eine Größe.
Die Koordinaten eines Unterelements beziehen sich immer auf die obere linke Ecke des
GUI Elements, zu dem sie gehören, unabhängig davon, ob dieses gescrollt wird. Dadurch
hängen die Koordinaten nicht von der aktuellen Scrollposition ab. Für Unterelemente,
für die Geometrie keinen Sinn macht oder nicht ermittelt werden kann, können
Koordinaten ignoriert werden und die Methoden getItemLocation
und getItemSize
sollten einfach [0,0] zurückliefern.
Die Methoden des Interface
de.qfs.apps.qftest.extensions.items.ItemResolver
teilen sich in drei
Kategorien auf: Erkennen eines Unterelements, die Beziehung zwischen einem
Unterelement und seinem Index und dem Auslesen von Informationen aus bzw. Ausführen
von Aktionen auf einem Unterelement.
|
|
|
Object getItem(Object element, int x, int y) |
Parameter |
element | Das GUI Element, dessen Unterelement bestimmt werden soll. |
x | Die X Koordinate relativ zum GUI Element. |
y | Die Y Koordinate relativ zum GUI Element. |
Rückgabewert |
Ein beliebiges Objekt, welches das Unterelement repräsentiert, oder null falls es
kein Unterelement an der angegebenen Stelle gibt.
|
|
int getItemCount(Object element, Object item) |
Parameter |
element | Das GUI Element, für das die Zahl der Unterelemente
ermittelt werden soll. |
item |
Null um die Anzahl der Unterelemente auf oberster Ebene zu erhalten, ein
Unterelement um die Anzahl von dessen Unterelementen der nächsten Ebene zu
ermitteln.
|
Rückgabewert |
Die Anzahl von Unterelementen oder -1 falls es keine weitere Ebene gibt.
|
|
Object getItemForIndex(Object element, SubItemIndex[] idx) |
Parameter |
element | Das GUI Element, dessen Unterelement bestimmt werden soll. |
idx | Der Index oder die Indizes für das Unterelement. |
Rückgabewert | Das Unterelement, das dem Index am besten entspricht. |
Exceptions |
IndexNotFoundException |
Falls kein Unterelement zum angegebenen Index passt. Verwenden Sie für diesen Fall
den Konstruktor
de.qfs.apps.qftest.shared.exceptions.IndexNotFoundException(SubItemIndex) .
|
|
SubItemIndex[] getItemIndex(Object element, Object item, int type) |
Parameter |
element | Das GUI Element, zu dem das Unterelement gehört. |
item | Das Unterelement dessen Index ermittelt werden soll. |
type |
Die Art des zu bestimmenden Index. Mögliche Werte sind INTELLIGENT ,
AS_STRING und AS_NUMBER , alle in der Klasse
SubItemIndex definiert. Sofern nicht nur eine Art von Index
unterstützt wird, sollte ein textueller Index für AS_STRING und ein
numerischer Index für AS_NUMBER geliefert werden. Beim Typ
INTELLIGENT können Sie frei entscheiden, welcher Art von Index das
Unterelement am besten repräsentiert. Auch ein gemischter Index ist möglich, z.B.
ein Spaltentitel plus numerischer Zeilenindex für eine Zelle in einer Tabelle.
|
Rückgabewert |
Ein Array von SutItemIndex Objekten. Aktuell sind nur Arrays mit
einem oder zwei Elementen erlaubt.
|
|
int[] getItemLocation(Object element, Object item) |
Parameter |
element | Das GUI Element, zu dem das Unterelement gehört. |
item | Das Unterelement dessen Position ermittelt werden soll. |
Rückgabewert |
Die Position des Unterelements als int Array der Form [X,Y]. Die Position muss
immer relativ zur oberen linken Ecke des GUI Elements sein, auch wenn diese Ecke
aktuell nicht sichtbar ist, z.B. wenn das GUI Element gescrollt wird. Für
Unterelemente ohne Geometrie liefern Sie einfach [0,0].
|
|
int[] getItemSize(Object element, Object item) |
Parameter |
element | Das GUI Element, zu dem das Unterelement gehört. |
item | Das Unterelement dessen Größe ermittelt werden soll. |
Rückgabewert |
Die Größe des Unterelements als int Array der Form [Breite,Höhe]. Für
Unterelemente ohne Geometrie liefern Sie einfach [0,0].
|
|
String getItemValue(Object element, Object item) |
Parameter |
element | Das GUI Element, zu dem das Unterelement gehört. |
item | Das Unterelement dessen Wert ermittelt werden soll. |
Rückgabewert |
Ein String entsprechend dem Wert des Unterelements, sein Inhalt, Label, etc.
|
|
Boolean repositionMouseEvent(Object element, Object item, int[] pos) |
Parameter |
element | Das GUI Element, zu dem das Unterelement gehört. |
item | Das Ziel-Unterelement für den Event. |
pos |
Die Koordinaten des Events als int Array der Form [X,Y]. Die Werte können direkt
im Array manipuliert werden. Sie können entweder gezielt Koordinaten für einen
bestimmten Punkt angeben oder [Integer.MAX_VALUE,Integer.MAX_VALUE] um die
Koordinaten zu ignorieren, so dass der Event später auf die Mitte abgespielt wird.
|
Rückgabewert |
Boolean.TRUE falls die Position geändert wurde, Boolean.FALSE falls die Position
nicht geändert wurde. Null um zu signalisieren, dass dieser Resolver das Element
nicht behandelt.
|
Exceptions |
BadItemException |
Falls das Element und der Item Typ nicht zusammen passen (sollte niemals passieren).
|
|
Boolean scrollItemVisible(Object element, Object item, int x, int y) |
Parameter |
element | Das GUI Element, zu dem das Unterelement gehört. |
item | Das Unterelement, das sichtbar gemacht werden soll. |
x |
Die X-Koordinate relativ zum Unterelement, die in jedem Fall sichtbar sein muss.
|
y |
Die Y-Koordinate relativ zum Unterelement, die in jedem Fall sichtbar sein muss.
|
Rückgabewert |
Boolean.TRUE falls die Scroll-Position des GUI Elements verändert wurde,
Boolean.FALSE falls die Scroll-Position unverändert ist oder nicht gescrollt
werden kann. Null um zu signalisieren, dass QF-Test das GUI Element selbst scrollen
soll.
|
|
void setIndexesResolved(int num) |
Parameter |
num |
Die Anzahl der aufgelösten Indizes.
|
|
|
|
Wie im vorhergehenden Abschnitt erklärt, repräsentiert ein
de.qfs.apps.qftest.shared.data.SubItemIndex
einen (partiellen) Index für
ein Unterelement eines GUI Elements. Diese Klasse definiert einige Konstanten mit
folgender Bedeutung:
- STRING
- Dies ist ein textueller Index
- NUMBER
- Dies ist ein numerischer Index
- REGEXP
- Dies ist ein regulärer Ausdruck, passend zu einem textuellen Index
- INTELLIGENT
- Ermittle den Typ von Index, der am besten zum Unterelement passt
- AS_STRING
- Ermittle einen textuellen Index
- AS_NUMBER
- Ermittle einen numerischen Index
Sie stellt außerdem folgende Methoden bereit:
|
|
|
SubItemIndex SubItemIndex(String index) |
Parameter |
index | Der textuelle Index. |
|
SubItemIndex SubItemIndex(int index) |
Parameter |
index | Der numerische Index. |
|
int asNumber() |
Rückgabewert | Der numerische Index. |
Exceptions |
IndexFormatException |
Falls der Index nicht vom Typ NUMBER ist oder nicht als Integer
geparst werden kann.
|
|
String getIndex() |
Rückgabewert | Der Index, konvertiert in einen String. |
|
String getType() |
Rückgabewert |
Der Typ des Index, STRING , NUMBER oder
REGEXP .
|
|
boolean matches(String name) |
Parameter |
name | Der zu prüfende Name. |
Rückgabewert |
True falls der Index nicht numerisch ist und zum angegebenen Namen passt.
|
Exceptions |
IndexFormatException |
Falls der Index einen ungültigen regulären Ausdruck enthält.
|
|
|
|
Wenn Ihr ItemResolver
implementiert und instanziiert ist, muss er bei der
ItemRegistry
registriert werden. Die Klasse
de.qfs.apps.qftest.extensions.items.ItemRegistry
bietet hierzu folgende
Methoden:
|
|
|
static ItemRegistry instance() |
Rückgabewert | Die ItemRegistry Singleton Instanz. |
|
void registerItemNameResolver2(Object element, ItemNameResolver2 resolver) |
Parameter |
element |
Das GUI Element für das registriert wird.
|
resolver |
Der zu registrierende Resolver.
|
|
void registerItemNameResolver2(String clazz, ItemNameResolver2 resolver) |
Parameter |
clazz |
Der Name der Klasse für die registriert wird.
|
resolver |
Der zu registrierende Resolver.
|
|
void registerItemResolver(Object element, ItemResolver resolver) |
Parameter |
element |
Das GUI Element für das registriert wird.
|
resolver |
Der zu registrierende Resolver.
|
|
void registerItemResolver(String clazz, ItemResolver resolver) |
Parameter |
clazz |
Der Name der Klasse für die registriert wird.
|
resolver |
Der zu registrierende Resolver.
|
|
void registerItemValueResolver2(Object element, ItemValueResolver2 resolver) |
Parameter |
element |
Das GUI Element für das registriert wird.
|
resolver |
Der zu registrierende Resolver.
|
|
void registerItemValueResolver2(String clazz, ItemValueResolver2 resolver) |
Parameter |
clazz |
Der Name der Klasse für die registriert wird.
|
resolver |
Der zu registrierende Resolver.
|
|
void unregisterItemNameResolver2(Object element, ItemNameResolver2 resolver) |
Parameter |
element |
Das GUI Element für das entfernt wird.
|
resolver |
Der zu entfernende Resolver.
|
|
void unregisterItemNameResolver2(String clazz, ItemNameResolver2 resolver) |
Parameter |
clazz |
Der Name der Klasse für die entfernt wird.
|
resolver |
Der zu entfernende Resolver.
|
|
void unregisterItemResolver(Object element, ItemResolver resolver) |
Parameter |
element |
Das GUI Element für das entfernt wird.
|
resolver |
Der zu entfernende Resolver.
|
|
void unregisterItemResolver(String clazz, ItemResolver resolver) |
Parameter |
clazz |
Der Name der Klasse für die entfernt wird.
|
resolver |
Der zu entfernende Resolver.
|
|
void unregisterItemValueResolver2(Object element, ItemValueResolver2 resolver) |
Parameter |
element |
Das GUI Element für das entfernt wird.
|
resolver |
Der zu entfernende Resolver.
|
|
void unregisterItemValueResolver2(String clazz, ItemValueResolver2 resolver) |
Parameter |
clazz |
Der Name der Klasse für die entfernt wird.
|
resolver |
Der zu entfernende Resolver.
|
|
|
|
Für die Implementierung der Interfaces ItemNameResolver2
,
ItemValueResolver2
und Checker
ist es wichtig, die Art der
Objekte zu kennen, die ein Unterelement intern repräsentieren, denn dies sind die
Objekte, die an die Methoden getItemName
, getItemValue
,
getCheckData
und getCheckDataAndItem
übergeben werden.
JavaFX
Die folgende Tabelle führt die komplexen Komponenten von JavaFX und die von QF-Test's
standard ItemResolvern
verwendete Repräsentation der jeweiligen
Unterelemente auf.
|
GUI element class |
Item type |
Accordion |
Integer Index |
ChoiceBox |
Integer Index |
ComboBox |
Integer Index |
ListView |
Integer Index |
TabPane |
Integer Index |
TableView |
int Array [column,row] wobei row < 0 eine ganze Spalte repräsentiert |
TableHeaderRow |
Integer Spaltenindex |
TextArea |
Integer Zeile |
TreeView |
TreeItem Objekt |
|
| | Tabelle 49.1: Interne Repräsentation für Unterlement von JavaFX Komponenten | |
Swing
Die folgende Tabelle führt die komplexen Komponenten von Swing und die von QF-Test's
standard ItemResolvern
verwendete Repräsentation der jeweiligen
Unterelemente auf.
|
GUI Element Klasse |
Art des Unterelements |
JComboBox |
Integer Index |
JList |
Integer Index |
JTabbedPane |
Integer Index |
JTable |
int Array [column,row] wobei row < 0 eine ganze Spalte repräsentiert |
JTableHeader |
Integer Spaltenindex |
JTextArea |
Integer Zeile |
JTree |
TreePath Pfad |
|
| | Tabelle 49.2: Interne Repräsentation für Unterlement von Swing Komponenten | |
SWT
Die folgende Tabelle führt die komplexen GUI Elemente von SWT und die von QF-Test's
standard ItemResolvern
verwendete Repräsentation der jeweiligen
Unterelemente auf.
|
GUI Element Klasse |
Art des Unterelements |
CCombo |
Integer Index |
Combo |
Integer Index |
CTabFolder |
Integer Index |
List |
Integer Index |
StyledText |
Integer Zeile |
TabFolder |
Integer Index |
Table |
int Array [Spalte,Zeile] oder nur Integer um eine Spalte
zu repräsentieren
|
Text |
Integer Zeile |
Tree |
Object Array [Integer Spalte,TreeItem
Zeile] oder nur Integer um eine Spalte zu repräsentieren
|
|
| | Tabelle 49.3: Interne Repräsentation für Unterlement von SWT GUI Elementen | |
Web
Die folgende Tabelle führt die komplexen GUI Elemente für Web und die von QF-Test's
standard ItemResolvern
verwendete Repräsentation der jeweiligen
Unterelemente auf.
|
GUI Element Klasse |
Art des Unterelements |
SELECT Knoten |
OPTION Knoten |
TEXTAREA Knoten |
Integer Zeile |
|
| | Tabelle 49.4: Interne Repräsentation für Unterlement von Web GUI Elementen | |