Chociaż jestem typowym backendowcem ostatnio miałem okazję dotknąć frontu i tematu testów w JavaScript.
Wybór padł na framework Cypress . Narzędzie – bo oprócz samego frameworka dostajemy też
specjalny program tzw. runner, do ich uruchamiania – zadebiutowało około 2 lat temu i od tego czasu zdobywa coraz większą popularność. Co prawda słyszałem kiedyś o Jasmine, ale wygląda na to, że Cypress jest o niebo lepszym wyborem, bo w jego przypadku dostajemy wszystko w jednej paczce. Na oficjalnej stronie jest to pięknie wyeksponowane „All-in-one testing framework, assertion library, with mocking and stubbing, all without Selenium.” Testy możemy uruchomić pod kontrolą różnych przeglądarek: Electron, Edge i Chrome, mamy dostęp do narzędzi deweloperskich i możemy bardzo łatwo podpiąć te testy pod CI. Dodatkowo w Cypress jest dostępny tzw. dashboard dla testów, w którym to są zbierana różne statystki, a także są rejestrowane wyniki uruchomienia testów (w postaci sreenshotów i filmików).

Nie będę tutaj robił wprowadzenia do tej biblioteki, za to chciałem się podzielić swoimi spostrzeżeniami w pracy z tą biblioteką i tym jak udało mi się rozwiązać kilka napotkanych problemów w integracji z biblioteką ExtJs. 

Po pierwsze miałem trudność z dostępem do zmiennych globalnych. Cała aplikacja zbudowana jest w oparciu o bibliotekę ExtJS i zaczynając swoją przygodę z testami pomyślałem, że fajnie byłoby mieć dostęp do tego API. Problem w tym, że Cypress nie widział głównego obiektu Ext’a (zmianna w scopie globalnym). Poniższym snippetem udało mi się do niego dobrać: 

let Ext; 
cy.window().then(win => {
   Ext = win.Ext; 
}); 

Mając już Exta mogłem trochę zaszaleć, potrzebowałem pobrać jakiś component – element UI. Zadanie miałem utrudnione, ponieważ komponentom nie zostały nadane identyfikatory. I tutaj do wyboru miałem API Exta oraz API Cypressa – wybrałem to drugie. Z tego powodu, że jego API, a dokładnie metoda cy.get() czeka, aż element będzie widoczny na stronie (można jej też przakazać jako parametr timeout – domyślnie czeka 4s). Jako selektor używana jest składnia taka sama co w jQuery. Poniższy zapis szuka elementu „span”, który zawiera tekst ‚Widgets’ (ten tekst może znajdować się również w elemencie podrzędnym).  Samo jQuery jest dostępne w cypress, jest owrapowane przez metodę cy.$$(), różnica w porównaniu do cy.get() jest taka, że ta pierwsza nie podejmuje kilku prób znalezienia elementu w drzewie DOM, nie wstrzymuje flowu testu. 

cy.get("span:contains('Widgets'):first")

Pokaże Wam jak udało mi się połączyć obie biblioteki na przykładzie okna modalnego, w którym wypełniam kilka pól i wciskam guzik ‚Save’. Najpierw znajduję okno popup i nadaję mu alias ‚popup’.  

cy.get('.x-window').as('popup');  

Następnie znajduję pole formularza w tym oknie popup. Używam jQuery, żeby znaleźć element, następnie jego id przekazuję, żeby utworzyć referencję do komponentu Exta i wywołuję (już z Ext’a) metodę komponentu setValue. 

cy.get('@popup').within( (x) => { 
  var inputForm = cy.$$(".x-form-item:contains('Manufacturer')")[0];
  var el = Ext.getCmp(inputForm.id);
  el.setValue(208); 
});

Po wszystkim, sprawdzam czy poprawnie wykonała się akcja. Kod asercji – głównie dla poprawy czytelności – wstawiłem w oddzielnym bloku then. Kompletne rozwiązanie prezentuje się w ten sposób.

it.only('Add competition number', () => {
   var pageUrl = 'some url';
   cy.visit(pageUrl); 
   let Ext;

   cy.window().then(win => {
      Ext = win.Ext;
      cy.get('.x-window').as('popup'); 
      
      cy.get('@popup').within( (x) => { 
        var inputForm = cy.$$(".x-form-item:contains('Manufacturer')")[0]; 
        var el = Ext.getCmp(inputForm.id); 
        el.setValue(208);
        // ....

        // save button
        var saveBtn = cy.contains("Save");
        saveBtn.click();
     });  
  }).then(win2 => 
   {
     cy.wait(1000);
     // assertion 
     cy.get('@widget').within( (el) => {
        cy.contains('some text').should('be.visible');
     });
   });
 })

Problemy, z którymi nadal się jeszcze mierzę, a dla których brakuje mi eleganckich rozwiązań to: 
– stosowanie cy.wait(), żeby Ext zdążył poprawnie zrenderować komponent 
– wywoływane asynchronicznie requsty z data storów (Ext), a flow testu (obecnie stosuję cy.wait)

Jeśli uda mi się znaleźć jakieś fajne rozwiązanie na powyższe problemy z pewnością napiszę. Póki co testy przechodzą, a ja zabieram się za pisanie kolejnych 😉

One Thought on “Cypress & ExtJS – testy e2e”

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *