Als ich letztens am Test-Harness einer Angular9-Komponente gearbeitet habe und die Reaktion auf die Tab-Taste automatisiert testen wollte, war ich doch sehr überrascht, dass dies wohl nach wie vor erst in End-2-End-Tests mit Protractor möglich ist, da die Tab-Taste im Unit-Test nicht simuliert wird. Eine Recherche im Netz ergab, dass das wohl nach wie vor als allgemeines Problem angesehen wird und die einzige Bibliothek (jquery-emulatetab) die ich gefunden habe, setzt auf jQuery auf – eine Abhängigkeit, die so gar nicht in den Stack dieses Angular-Projekts hineinpasst. Da habe ich mich gefragt, ob man ein ähnliches Tool nicht ohne größere Abhängigkeiten umsetzen könnte. Nach einer kurzen Evaluations-Phase war klar, dass dies mit wenigen Zeilen Code gelingen könnte und um das Ergebnis vorweg zu greifen, dieses Tool nennt sich nun „emulate-tab“, bedient sich lediglich nativer Browser-Funktionen und ist per npm unter MIT-Lizenz verfügbar.
Die Herausforderungen dabei bestanden zum einen darin, mögliche Tab-Ziele und deren Reihenfolge zu ermitteln. Dabei stützt sich das Tool auf die „tabindex“-Property, die manuell oder vom Browser gesetzt wird. Anschließend werden noch einige Elemente, beispielsweise unsichtbare Eingabefelder aussortiert. Zum anderen müssen alle Events simuliert werden, die der Browser bei einem echten Tastendruck auslösen würde. Angefangen beim „keydown“ über „blur“, „focus“ und „keyup“ bis hin zum selektieren von Inhalten im Zielfeld. Insgesamt stellten sich diese aber als wesentlich unkomplizierter heraus, als im Vorfeld befürchtet. So dass die eigentliche Komplexität im Verifizieren des Verhaltens in unterschiedlichen Testumgebungen bestand.
Die Beispielprojekte in Angular-9 mit Material Komponenten, Typescript und RequireJs und purem HTML mit JavaScript mit entsprechenden jeweils automatisierten Testumgebungen (alle karma/Jasmine) zeigen, dass sich das Tool nicht nur in Angular, sondern auch jeder anderen Testumgebung einsetzen lässt. Im ursprünglichem (wesentlich größeren) Projekt setzte ich auch Jest als Test-Runner ein. Selbst npm ist nicht zwingend erforderlich – das Script lässt sich auch direkt auf der GitHub-Release-Seite herunterladen und so in andere Projekte einbinden.
Nicht nur für Tests – auch bei der Verbesserung der UX auf produktiven Seiten, kann „emulate-tab“ unterstützen. So habe ich beispielsweise einen Fall, in dem ein Button nach dem drücken deaktiviert wird. Das gewollte Verhalten wäre, dass dabei ein beliebiges Feld nach dem Button in den Fokus rückt – das native Verhalten des Browsers selektiert aber das Feld vor dem Button. Da es sich um eine Wiederverwendbare Komponente handelt, ist das Feld nach dem Button nicht bekannt. Durch ein „emulateTab()“ nach dem deaktivieren des Buttons, stellt dies nun keine Herausforderung mehr dar.
Fazit: Das programmatische auslösen einer Tab-Navigation auf Web-Seiten sollte mit „emulate-tab“, einem einfachem kleinen Tool ohne Abhängigkeiten, zukünftig kein größeres Problem mehr darstellen.