Tests responsiver Anwendungen mit dem Galen Framework

Seite 3: Wiederkehrendes

Inhaltsverzeichnis

Eine Komponente kann dagegen mehrmals während der Ausführung vorkommen. Eine Liste mit Datensätzen hat üblicherweise immer denselben Grundaufbau. Eine Komponente spezifiziert ein einzelnes Element. Im folgenden Beispiel bildet sie die Bootstrap-Callout-Vorlage ab, die immer eine h4-Überschrift und einen Textblock hat:

Die sogenannte Vorlage "Bootstrap Callout"

Komponenten erlauben die bequeme Iteration über Listen. Zudem ermöglichen sie die Referenz auf das Elternelement über den Pseudo-Selektor parent. Ansonsten verhalten sie sich wie normale Specs. Ihre Nutzung bietet sich bei jeder Art von wiederverwendbaren Elementen an, die als Widgets eingebunden sind (z. B. Menüeinträge). Das folgende Beispiel prüft, ob die Überschrift in der Komponente innerhalb des Elternelements mit 10 Pixel Abstand links und rechts liegt und zwischen 90 und 100 Prozent der Bildschirmbreite einnimmt. Gleiches soll auch für die Textblöcke gelten.

Bei der Selektion mehrerer Elemente erlaubt der *-Selektor den Zugriff auf einzelne Objekte:

@objects
heading h4
text-* p
= callout specification =
@on phone
heading:
inside parent 10px top left
width 90 to 100% of screen/width
text-*:
inside parent 10px top left
width 90 to 100% of screen/width
  @on tablet
heading:
inside parent 10px top left
width 80 to 90% of screen/width
text-*:
inside parent 10px top left
width 80 to 90% of screen/width
  @on desktop
heading:
inside: parent 10px top left
width: 75 to 85% of screen/width
text-*:
inside: parent 10px top left
width: 75 to 85% of screen/width

Gerade bei umfangreichen Layouts ergeben sich häufig Patterns, die Tester wiederverwenden möchten. Dafür bietet das Framework sogenannte "custom rules" an. Das Prinzip ist ähnlich wie in Cucumber: Das Framework ruft über Matching die entsprechende Regel auf:

# Check elements horizontal alignment top e.g.
#
# = footer items =
# @on *
# | legal-and-contact.item-* are aligned
# horizontally top next to each other
@rule %{objectPattern} are aligned horizontally top next to each other
@if ${count(objectPattern) > 1}
@forEach [${objectPattern}] as item, next as nextItem
${item}:
aligned horizontally top ${nextItem}

Damit können Tester die Spezifikationen gut lesbar und wiederverwendbar gestalten. Für komplexere Regeln nutzen sie JavaScript-Funktionen:

/**
* This is a high-level spec for checking that elements
* are displayed in table layout
* with different margins for vertical and horizontal sides
* e.g.
* | item-* are rendered in 2 column table layout,
* with 0 to 4px vertical and 1px horizontal margin
rule("%{objectPattern} are rendered in %{columns: [0-9]+}
column table layout,
with %{verticalMargin} vertical
and %{horizontalMargin} horizontal margin",
function (objectName, parameters) {
_ruleRenderedInTable(this,
parameters.itemPattern,
parseInt(columns),
parameters.verticalMargin,
parameters.horizontalMargin);
});

Die Nutzung innerhalb von Spec-Dateien erwartet für Custom Rules ein vorangestelltes Pipe-Symbol:

@objects
login_box #login
label-* .label
username #username
password #password
submit button
# Now you can group objects like this
@groups
login_box_elements login.label-*,login.username,
login.password,login.submit
login_box_textfields login.label-*
login_box_buttons login.submit
@set
general_margin 10 to 20px
general_padding ~ 1px
= Login box looks good =
login_box:
visible
login_box.label-* are rendered in 2 column table layout, \
with ${general_margin} vertical and ${general_margin} \
horizontal margin
| at the top inside content with ${general_margin} margin \
centered horizontally inside content ${general_padding}
@on desktop, tablet
| &login_box_elements are aligned horizontally top next to \
each other

Das Listing verwendet Variablen und Gruppen. Letztere fassen Objekte zusammen, die sich nicht über Selektoren adressieren lassen.