Wyszukaj / o blogu

Cypress: wprowadzenie cz. 2

Opublikowano ndz 16 stycznia 2022 w qa • 3 min read

cypress

Cypress i tworzenie ciągów

Cypress oparty jest o tworzenie ciągów (łączeniu ze sobą funkcji w celu stworzenia testu). Cypress sam zajmuje się Promisami.

cy.get('textarea.post-body')
    .type('{enter}')

cy.get('textarea.post-body')
    .contains('xyz')
    .click()

cy.get('textarea.post-body')
    .find('.productname')
    .eq(1)
    .click()

cy.find() - szuka dziecka w wybranego elementu na podstawie selektora

Przykładowe funkcje, które pozwalają na interakcję z testowaną stroną -

.blur()- Blur na wcześniej sfokusowanym elemencie DOM.

.focus() - Focus na wybranych elemencie DOM.

.clear() - Czyści input lub textarea.

.check() - Zaznacza checkbox(es) or radio(s).

.uncheck() - Odznacza checkbox(es).

.select() - Select an <option> within a <select>.

.dblclick() - podwójny-click na wybrany element DOM.

.rightclick() - prawy-klik na wybrany element DOM.


Cypress i jego Asynchroniczność

Cypressowe komendy są asynchroniczne (JS jest jednowątkowy, a Cypress jest na Node.js) - jakikolwiek krok jest możliwy do egzekucji to zostanie wykonany/ może wykonywać więcej niż jeden wątek na raz - cypressowe funkcje nie są wykonywane w trakcie wywołania a są kolejkowane w celu ich wykonania. Można mieć wpływ na kolejność wykonywania się testów poprzez użycie then.

Cypress promise i then

Cypressowe komendy działają na zasadzie promisa/ są promisami.

then jest używany w przypadku pozytywnego wyniku promisa (resolve), catch w przypadku negatywnego (reject).

    let promise = new Promise((resolve, reject) => {
        let a = 1 + 1
        if(a == 2) {
            resolve('Promise Fulfilled')
        } else {
            reject('Promise not fulfilled')
        }
    })

    promise.then((message) => {
        console.log(message + ', promise has passed!')
    }).catch((message) => {
        console.log(message + ', promise has failed')
    })

Cy.then() - pozwala na pracę z obiektem dostarczonym poprzez poprzedzającą funkcję np. cy.get()

    cy.get('xyz').then(($xyz) => {}) // 1) dostarcza xyz 2) wykonuje na xyz funkcję

Stosowanie zmiennych

W kontekście stosowania zmiennych należy mieć na uwadze asynchroniczność oraz promisy (dlatego aby wykorzystać obiekt, który cypress zwraca należy wykorzystać komendę then lub zasadę łańcuchowania/łączenia ciągów komend).

    //To podejście nie zadziała, ponieważ cy.get() zwraca Promise
    const header = cy.get("div .header");
    cy.log(header.text())

    //To podejście zadziała, ponieważ cy.get() jest odpowiednio obsłużony poprzez zastosowanie cy.then()
    cy.get("div .header")
        .then($header => {
            const headerText = $header.text()
            expect(headerText).to.equal("xyz")
        })

faktycznie w przypadku cypressa aliasy zastępują zmienne - patrz przykłady zastosowania alias i iframe


Iteracje .each()

.each(callBackFn)- iteruje po wszystkich elementach znajdujących się w tablicy wykonując na nich przypisaną funkcję / podobnie jak JS forEeach().


Wrap() 🎁

.wrap() - zwraca obiekt, który pozwala na wykonanie cypressowej komendy -> opakowuje wybraną zmienną, po którą jest zapisane odniesienie do elementu DOM tak aby zastosować cypressową komendy -> pozwala na rozróżnienie cy.click() od js.click()


invoke

invoke() - pozwala na wywołanie właściwości danego elementu DOM.

    cy.get('button').invoke('text').then((text) => {
        expect(text).to.equal('xyz')
    })

Alias

.as() - jest swego rodzaju cypressową zmienną - pozwala na odwołanie się do przechowywanej wartości w innej części kodu. Odwołanie się do aliasy zależne jest od kontekstu i wymaga wskazania poprzez this lub @

    cy.get('button').inovke('text').as('buttonText')

    this.buttonText

    cy.get('@buttonText')

Przeglądarka

Wiele tabów w przeglądarce

Cypress nie obsługuje wielu tabów w przeglądarce - obejściem tej zasady jest wyświetlenie wszystkich możliwych informacji w pojedynczym oknie przeglądarki - w przypadku linków może oznaczać to usunięcie atrybutu odpowiedzialnego za wyświetlenie treści w nowym oknie.

    cy.get('#xyz').invoke('removeAttr', 'target').click({force:true})

Akcje przeglądarki

Cypressowe akcje pozwalają na sterowanie przeglądarką: do przodu, do tyłu, przeładuj

    cy.go('back')
    cy.reload()
    cy.reload(true) //przeładuj nie korzystając z cache

    cy.go('forward')
    cy.url().should('include', 'xyz')

Alerty - cy.on() 🚨

Cypress automatycznie zwraca alerty jako true - stąd aby stworzyć asercję lub zwrócić inny wynik alertu należy 'wyłapać' pojawienie się okna alertu oraz zwrócić false

    cy.on('window:alert', (str) => {
        expect(str).to.equal('I am an alert box!')
    })

    cy.on('window:alert', (str) => {
        return false;
    })

iframe 🖼

Cypress nie obsługuje iframe stad testowanie wymaga wydobycia informacji

    cy.get('#iframe').invoke('removeAttr', 'target').click({force:true})

    cy.get('#frame').then($iframe => {
        const body = $iframe.contents().find('body')
        cy.wrap(body).as('iframe')
    })

    cy.get('@iframe').find('#button').click()

    cy.get('@iframe').find('#qwer').as('qwer')

    cy.get('@qwerl').should(($expectedText) => {
        const text = $expectedText.text()
        expect(text).to.include('XYZ');
    })

    cy.get('@qwer').contains('Close').click()

Obsługa checkboxów - cy.check() ☑

    cy.get('#radio-buttons').find("[type='radio']").first().check()

    cy.get('#radio-buttons').find("[type='radio']").eq(1).check()

    cy.get('#radio-buttons').find("[type='radio']").eq(2).uncheck()

    cy.get("[value='a']").should('be.checked')

    cy.get("[value='b']").should('not.be.checked')

    cy.get("[value='c']").should('be.disabled')

cy.trigger() ⌨

wywołanie wydarzenia na elementach drzewa DOM - np. w celu wywołania zdarzeń jak np. kliknięcia, wpisywania tekstu, wyboru opcji, itp. Wymaga wcześniejszego wywołania elementu (podpięcia pod element niosący element DOM)

    cy.get('#radio-buttons').find("[type='radio']").first().trigger('click')
    $el.trigger('click')
    $el.trigger('keydown')
    $el.trigger('keypress')
    $el.trigger('keyup')
    $el.trigger('focus')
    $el.trigger('blur')
    $el.trigger('change')

akcje myszy 🐁

Kliknięcie myszy na środek elementu ({which: 1})

    cy.get('#draggable').trigger('mousedown', {which: 1});

Kliknięcie mysz w określonym miejscu elementu przez podanie współrzędnych

    cy.get('#draggable').trigger('mousemove', {clientX: 100, clientY: 100});

Przenieś mysz (1) a następnie upuść (2) na element

    cy.get('#droppable').trigger('mousemove').trigger('mouseup', {force:true})

    cy.get('#droppable').dblclick()

Sprawdzenie czy kliknięty element posiada konkretny css

    cy.get('#click-box').trigger('mousedown', {which: 1}).then(($element) => {
        expect($element).to.have.css('background-color', 'rgb(0, 255, 0)')
    })

cypress real events

Podstawowe eventy cypressa są symulowane - wszystkie eventy pochodzą z JS, ich zachowanie może się trochę różnić od prawdziwych zachowań, a część zachowań nie może być w ogóle symulowana np. wpisywanie informacji do alertów lub kopiowanie - wtyczka cypress-real-events ma za zadanie odpowiedzieć na te problemy.

Więcej na temat zastosowanie tej wtyczki przeczytasz tutaj Cypress Real Events Plugin


Źródła:

Cypress Real Events - github.io

Cypress Real Events Plugin

cypress.io