Expect
Podczas pisania testów często musisz sprawdzać, czy wartości spełniają określone warunki. expect
daje ci dostęp do wielu "dopasowań" (matchers), które pozwalają na sprawdzanie różnych rzeczy na obiektach browser
, element
lub mock
.
Domyślne opcje
Poniższe domyślne opcje są powiązane z opcjami waitforTimeout
i waitforInterval
ustawionymi w konfiguracji.
Ustaw poniższe opcje tylko wtedy, gdy chcesz czekać określony czas dla swoich asercji.
{
wait: 2000, // ms do czekania na spełnienie oczekiwania
interval: 100, // interwał między próbami
}
Jeśli chcesz wybrać inne czasy oczekiwania i interwały, ustaw te opcje w następujący sposób:
// wdio.conf.js
import { setOptions } from 'expect-webdriverio'
export const config = {
// ...
before () {
setOptions({ wait: 5000 })
},
// ...
}
Opcje dopasowań
Każde dopasowanie może przyjmować kilka opcji, które pozwalają na modyfikację asercji:
Opcje komend
Nazwa | Typ | Szczegóły |
---|---|---|
wait | number | czas w ms do czekania na spełnienie oczekiwania. Domyślnie: 3000 |
interval | number | interwał między próbami. Domyślnie: 100 |
beforeAssertion | function | funkcja wywoływana przed wykonaniem asercji |
afterAssertion | function | funkcja wywoływana po wykonaniu asercji, zawierająca wyniki asercji |
message | string | wiadomość użytkownika dodawana przed błędem asercji |
Opcje ciągów znaków
Opcje te mogą być stosowane dodatkowo do opcji komend podczas sprawdzania ciągów znaków.
Nazwa | Typ | Szczegóły |
---|---|---|
ignoreCase | boolean | zastosuj toLowerCase zarówno do wartości rzeczywistych, jak i oczekiwanych |
trim | boolean | zastosuj trim do wartości rzeczywistej |
replace | Replacer | Replacer[] | zamień części wartości rzeczywistej, które pasują do ciągu znaków/RegExp. Zamiennikiem może być ciąg znaków lub funkcja. |
containing | boolean | oczekuj, że wartość rzeczywista zawiera wartość oczekiwaną, w przeciwnym razie — ścisła równość. |
asString | boolean | może być pomocne do wymuszenia konwersji wartości właściwości na ciąg znaków |
atStart | boolean | oczekuj, że wartość rzeczywista rozpoczyna się od wartości oczekiwanej |
atEnd | boolean | oczekuj, że wartość rzeczywista kończy się wartością oczekiwaną |
atIndex | number | oczekuj, że wartość rzeczywista ma wartość oczekiwaną pod danym indeksem |
Opcje liczbowe
Opcje te mogą być stosowane dodatkowo do opcji komend podczas sprawdzania liczb.
Nazwa | Typ | Szczegóły |
---|---|---|
eq | number | równa się |
lte | number | mniejsza lub równa |
gte | number | większa lub równa |
Obsługa encji HTML
Encja HTML to fragment tekstu ("ciąg znaków"), który zaczyna się od znaku ampersand (&
) i kończy średnikiem (;
). Encje są często używane do wyświetlania znaków zastrzeżonych (które w przeciwnym razie byłyby interpretowane jako kod HTML) oraz niewidocznych znaków (jak niełamliwe spacje, np.
).
Aby znaleźć lub wejść w interakcję z takim elementem, użyj odpowiednika Unicode dla encji, np.:
<div data="Some Value">Some Text</div>
const myElem = await $('div[data="Some\u00a0Value"]')
await expect(myElem).toHaveAttribute('data', 'div[Some\u00a0Value')
await expect(myElem).toHaveText('Some\u00a0Text')
Wszystkie odniesienia do znaków Unicode można znaleźć w specyfikacji HTML.
Uwaga: Unicode jest niewrażliwy na wielkość liter, więc zarówno \u00a0
, jak i \u00A0
działają. Aby znaleźć element w przeglądarce, usuń u
z kodu Unicode, np.: div[data="Some\00a0Value"]
Dopasowania dla przeglądarki
toHaveUrl
Sprawdza, czy przeglądarka jest na określonej stronie.
Użycie
await browser.url('https://webdriver.io/')
await expect(browser).toHaveUrl('https://webdriver.io')
Użycie
await browser.url('https://webdriver.io/')
await expect(browser).toHaveUrl(expect.stringContaining('webdriver'))
toHaveTitle
Sprawdza, czy strona ma określony tytuł.
Użycie
await browser.url('https://webdriver.io/')
await expect(browser).toHaveTitle('WebdriverIO · Next-gen browser and mobile automation test framework for Node.js')
await expect(browser).toHaveTitle(expect.stringContaining('WebdriverIO'))
toHaveClipboardText
Sprawdza, czy przeglądarka ma określony tekst zapisany w schowku.
Użycie
import { Key } from 'webdriverio'
await browser.keys([Key.Ctrl, 'a'])
await browser.keys([Key.Ctrl, 'c'])
await expect(browser).toHaveClipboardText('some clipboard text')
await expect(browser).toHaveClipboardText(expect.stringContaining('clipboard text'))
Dopasowania dla elementów
toBeDisplayed
Wywołuje isDisplayed
na danym elemencie.
Użycie
const elem = await $('#someElem')
await expect(elem).toBeDisplayed()
toExist
Wywołuje isExisting
na danym elemencie.
Użycie
const elem = await $('#someElem')
await expect(elem).toExist()
toBePresent
To samo co toExist
.
Użycie
const elem = await $('#someElem')
await expect(elem).toBePresent()
toBeExisting
To samo co toExist
.
Użycie
const elem = await $('#someElem')
await expect(elem).toBeExisting()
toBeFocused
Sprawdza, czy element ma fokus. Ta asercja działa tylko w kontekście strony internetowej.
Użycie
const elem = await $('#someElem')
await expect(elem).toBeFocused()
toHaveAttribute
Sprawdza, czy element ma określony atrybut z konkretną wartością.
Użycie
const myInput = await $('input')
await expect(myInput).toHaveAttribute('class', 'form-control')
await expect(myInput).toHaveAttribute('class', expect.stringContaining('control'))
toHaveAttr
To samo co toHaveAttribute
.
Użycie
const myInput = await $('input')
await expect(myInput).toHaveAttr('class', 'form-control')
await expect(myInput).toHaveAttr('class', expect.stringContaining('control'))
toHaveElementClass
Sprawdza, czy element ma pojedynczą nazwę klasy. Może być również wywołany z tablicą jako parametrem, gdy element może mieć wiele nazw klas.
Użycie
const myInput = await $('input')
await expect(myInput).toHaveElementClass('form-control', { message: 'Not a form control!' })
await expect(myInput).toHaveElementClass(['form-control' , 'w-full'], { message: 'not full width' })
await expect(myInput).toHaveElementClass(expect.stringContaining('form'), { message: 'Not a form control!' })
toHaveElementProperty
Sprawdza, czy element ma określoną właściwość.
Użycie
const elem = await $('#elem')
await expect(elem).toHaveElementProperty('height', 23)
await expect(elem).not.toHaveElementProperty('height', 0)
toHaveValue
Sprawdza, czy element wejściowy ma określoną wartość.
Użycie
const myInput = await $('input')
await expect(myInput).toHaveValue('admin-user', { ignoreCase: true })
await expect(myInput).toHaveValue(expect.stringContaining('user'), { ignoreCase: true })
toBeClickable
Sprawdza, czy element może zostać kliknięty, wywołując na nim isClickable
.
Użycie
const elem = await $('#elem')
await expect(elem).toBeClickable()
toBeDisabled
Sprawdza, czy element jest wyłączony, wywołując na nim isEnabled
.
Użycie
const elem = await $('#elem')
await expect(elem).toBeDisabled()
// to samo co
await expect(elem).not.toBeEnabled()
toBeEnabled
Sprawdza, czy element jest włączony, wywołując na nim isEnabled
.
Użycie
const elem = await $('#elem')
await expect(elem).toBeEnabled()
// to samo co
await expect(elem).not.toBeDisabled()
toBeSelected
Sprawdza, czy element jest wybrany, wywołując na nim isSelected
.
Użycie
const elem = await $('#elem')
await expect(elem).toBeSelected()
toBeChecked
To samo co toBeSelected
.
Użycie
const elem = await $('#elem')
await expect(elem).toBeChecked()
toHaveComputedLabel
Sprawdza, czy element ma określoną obliczoną etykietę WAI-ARIA. Może być również wywołany z tablicą jako parametrem w przypadku, gdy element może mieć różne etykiety.
Użycie
await browser.url('https://webdriver.io/')
const elem = await $('a[href="https://github.com/webdriverio/webdriverio"]')
await expect(elem).toHaveComputedLabel('GitHub repository')
await expect(elem).toHaveComputedLabel(expect.stringContaining('repository'))
Użycie
await browser.url('https://webdriver.io/')
const elem = await $('a[href="https://github.com/webdriverio/webdriverio"]')
await expect(elem).toHaveComputedLabel(['GitHub repository', 'Private repository'])
await expect(elem).toHaveComputedLabel([expect.stringContaining('GitHub'), expect.stringContaining('Private')])
toHaveComputedRole
Sprawdza, czy element ma określoną obliczoną rolę WAI-ARIA. Może być również wywołany z tablicą jako parametrem w przypadku, gdy element może mieć różne etykiety.
Użycie
await browser.url('https://webdriver.io/')
const elem = await $('[aria-label="Skip to main content"]')
await expect(elem).toHaveComputedRole('region')
await expect(elem).toHaveComputedRole(expect.stringContaining('ion'))
Użycie
await browser.url('https://webdriver.io/')
const elem = await $('[aria-label="Skip to main content"]')
await expect(elem).toHaveComputedRole(['region', 'section'])
await expect(elem).toHaveComputedRole([expect.stringContaining('reg'), expect.stringContaining('sec')])
toHaveHref
Sprawdza, czy element typu link ma określony cel linku.
Użycie
const link = await $('a')
await expect(link).toHaveHref('https://webdriver.io')
await expect(link).toHaveHref(expect.stringContaining('webdriver.io'))
toHaveLink
To samo co toHaveHref
.
Użycie
const link = await $('a')
await expect(link).toHaveLink('https://webdriver.io')
await expect(link).toHaveLink(expect.stringContaining('webdriver.io'))
toHaveId
Sprawdza, czy element ma określony atrybut id
.
Użycie
const elem = await $('#elem')
await expect(elem).toHaveId('elem')
toHaveText
Sprawdza, czy element ma określony tekst. Może być również wywołany z tablicą jako parametrem w przypadku, gdy element może mieć różne teksty.
Użycie
await browser.url('https://webdriver.io/')
const elem = await $('.container')
await expect(elem).toHaveText('Next-gen browser and mobile automation test framework for Node.js')
await expect(elem).toHaveText(expect.stringContaining('test framework for Node.js'))
await expect(elem).toHaveText(['Next-gen browser and mobile automation test framework for Node.js', 'Get Started'])
await expect(elem).toHaveText([expect.stringContaining('test framework for Node.js'), expect.stringContaining('Started')])
W przypadku, gdy w div znajduje się lista elementów:
<ul>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ul>
Możesz sprawdzić je za pomocą tablicy:
const elem = await $$('ul > li')
await expect(elem).toHaveText(['Coffee', 'Tea', 'Milk'])
toHaveHTML
Sprawdza, czy element ma określony tekst. Może być również wywołany z tablicą jako parametrem w przypadku, gdy element może mieć różne teksty.
Użycie
await browser.url('https://webdriver.io/')
const elem = await $('.hero__subtitle')
await expect(elem).toHaveHTML('<p class="hero__subtitle">Next-gen browser and mobile automation test framework for Node.js</p>')
await expect(elem).toHaveHTML(expect.stringContaining('Next-gen browser and mobile automation test framework for Node.js'))
await expect(elem).toHaveHTML('Next-gen browser and mobile automation test framework for Node.js', { includeSelectorTag: false })
Użycie
await browser.url('https://webdriver.io/')
const elem = await $('.hero__subtitle')
await expect(elem).toHaveHTML(['Next-gen browser and mobile automation test framework for Node.js', 'Get Started'], { includeSelectorTag: false })
await expect(elem).toHaveHTML([expect.stringContaining('automation test framework for Node.js'), expect.stringContaining('Started')], { includeSelectorTag: false })
toBeDisplayedInViewport
Sprawdza, czy element znajduje się w obszarze widocznym, wywołując na nim isDisplayedInViewport
.
Użycie
const elem = await $('#elem')
await expect(elem).toBeDisplayedInViewport()
toHaveChildren
Sprawdza liczbę dzieci pobranego elementu poprzez wywołanie komendy element.$('./*')
.
Użycie
const list = await $('ul')
await expect(list).toHaveChildren() // lista ma co najmniej jeden element
// to samo co
await expect(list).toHaveChildren({ gte: 1 })
await expect(list).toHaveChildren(3) // lista ma 3 elementy
// to samo co
await expect(list).toHaveChildren({ eq: 3 })
toHaveWidth
Sprawdza, czy element ma określoną szerokość.
Użycie
await browser.url('http://github.com')
const logo = await $('.octicon-mark-github')
await expect(logo).toHaveWidth(32)
toHaveHeight
Sprawdza, czy element ma określoną wysokość.
Użycie
await browser.url('http://github.com')
const logo = await $('.octicon-mark-github')
await expect(logo).toHaveHeight(32)
toHaveSize
Sprawdza, czy element ma określony rozmiar.
Użycie
await browser.url('http://github.com')
const logo = await $('.octicon-mark-github')
await expect(logo).toHaveSize({ width: 32, height: 32 })
toBeElementsArrayOfSize
Sprawdza liczbę pobranych elementów za pomocą komendy $$
.
Uwaga: To dopasowanie zaktualizuje przekazaną tablicę najnowszymi elementami, jeśli asercja się powiedzie. Jednak jeśli przypisałeś zmienną ponownie, musisz pobrać elementy ponownie.
Użycie
const listItems = await $$('ul>li')
await expect(listItems).toBeElementsArrayOfSize(5) // 5 elementów na liście
await expect(listItems).toBeElementsArrayOfSize({ lte: 10 })
// to samo co
assert.ok(listItems.length <= 10)
Dopasowania sieciowe
toBeRequested
Sprawdza, czy atrapa (mock) została wywołana
Użycie
const mock = browser.mock('**/api/todo*')
await expect(mock).toBeRequested()
toBeRequestedTimes
Sprawdza, czy atrapa (mock) została wywołana oczekiwaną liczbę razy
Użycie
const mock = browser.mock('**/api/todo*')
await expect(mock).toBeRequestedTimes(2) // await expect(mock).toBeRequestedTimes({ eq: 2 })
await expect(mock).toBeRequestedTimes({ gte: 5, lte: 10 }) // żądanie wywołane co najmniej 5 razy, ale mniej niż 11
toBeRequestedWith
Sprawdza, czy atrapa (mock) została wywołana zgodnie z oczekiwanymi opcjami.
Większość opcji obsługuje częściowe dopasowania expect/jasmine, takie jak expect.objectContaining
Użycie
const mock = browser.mock('**/api/todo*', { method: 'POST' })
await expect(mock).toBeRequestedWith({
url: 'http://localhost:8080/api/todo', // [opcjonalnie] string | function | custom matcher
method: 'POST', // [opcjonalnie] string | array
statusCode: 200, // [opcjonalnie] number | array
requestHeaders: { Authorization: 'foo' }, // [opcjonalnie] object | function | custom matcher
responseHeaders: { Authorization: 'bar' }, // [opcjonalnie] object | function | custom matcher
postData: { title: 'foo', description: 'bar' }, // [opcjonalnie] object | function | custom matcher
response: { success: true }, // [opcjonalnie] object | function | custom matcher
})
await expect(mock).toBeRequestedWith({
url: expect.stringMatching(/.*\/api\/.*/i),
method: ['POST', 'PUT'], // POST lub PUT
statusCode: [401, 403], // 401 lub 403
requestHeaders: headers => headers.Authorization.startsWith('Bearer '),
postData: expect.objectContaining({ released: true, title: expect.stringContaining('foobar') }),
response: r => Array.isArray(r) && r.data.items.length === 20
})
Dopasowanie zrzutów ekranu
WebdriverIO obsługuje podstawowe testy zrzutów ekranu (snapshot tests), a także testy zrzutów DOM.
toMatchSnapshot
Sprawdza, czy dowolny obiekt pasuje do określonej wartości. Jeśli przekażesz WebdriverIO.Element
, automatycznie zostanie wykonany zrzut stanu outerHTML
.
Użycie
// zrzut dowolnych obiektów (nie wymagane tutaj "await")
expect({ foo: 'bar' }).toMatchSnapshot()
// zrzut `outerHTML` WebdriverIO.Element (zrzut DOM, wymaga "await")
await expect($('elem')).toMatchSnapshot()
// zrzut wyniku komendy elementu
await expect($('elem').getCSSProperty('background-color')).toMatchSnapshot()
toMatchInlineSnapshot
Podobnie możesz użyć toMatchInlineSnapshot()
, aby zapisać zrzut bezpośrednio w pliku testowym. Na przykład:
await expect($('img')).toMatchInlineSnapshot()
Zamiast tworzyć plik zrzutu, WebdriverIO bezpośrednio zmodyfikuje plik testowy, aby zaktualizować zrzut jako ciąg znaków:
await expect($('img')).toMatchInlineSnapshot(`"<img src="/public/apple-touch-icon-precomposed.png">"`)
Dopasowania dla zrzutów wizualnych
Poniższe dopasowania są zaimplementowane jako część wtyczki @wdio/visual-service
i są dostępne tylko wtedy, gdy usługa jest skonfigurowana. Upewnij się, że postępujesz zgodnie z instrukcjami konfiguracji.
toMatchElementSnapshot
Sprawdza, czy dany element pasuje do zrzutu z linii bazowej.
Użycie
await expect($('.hero__title-logo')).toMatchElementSnapshot('wdioLogo', 0, {
// opcje
})
Oczekiwany wynik to domyślnie 0
, więc możesz napisać tę samą asercję jako:
await expect($('.hero__title-logo')).toMatchElementSnapshot('wdioLogo', {
// opcje
})
lub nie przekazywać żadnych opcji:
await expect($('.hero__title-logo')).toMatchElementSnapshot()
toMatchScreenSnapshot
Sprawdza, czy bieżący ekran pasuje do zrzutu z linii bazowej.
Użycie
await expect(browser).toMatchScreenSnapshot('partialPage', 0, {
// opcje
})
Oczekiwany wynik to domyślnie 0
, więc możesz napisać tę samą asercję jako:
await expect(browser).toMatchScreenSnapshot('partialPage', {
// opcje
})
lub nie przekazywać żadnych opcji:
await expect(browser).toMatchScreenSnapshot('partialPage')
toMatchFullPageSnapshot
Sprawdza, czy zrzut pełnej strony pasuje do zrzutu z linii bazowej.
Użycie
await expect(browser).toMatchFullPageSnapshot('fullPage', 0, {
// opcje
})
Oczekiwany wynik to domyślnie 0
, więc możesz napisać tę samą asercję jako:
await expect(browser).toMatchFullPageSnapshot('fullPage', {
// opcje
})
lub nie przekazywać żadnych opcji:
await expect(browser).toMatchFullPageSnapshot('fullPage')
toMatchTabbablePageSnapshot
Sprawdza, czy zrzut pełnej strony zawierający oznaczenia kartek pasuje do zrzutu z linii bazowej.
Użycie
await expect(browser).toMatchTabbablePageSnapshot('tabbable', 0, {
// opcje
})
Oczekiwany wynik to domyślnie 0
, więc możesz napisać tę samą asercję jako:
await expect(browser).toMatchTabbablePageSnapshot('tabbable', {
// opcje
})
lub nie przekazywać żadnych opcji:
await expect(browser).toMatchTabbablePageSnapshot('tabbable')
Używanie wyrażeń regularnych
Możesz również bezpośrednio używać wyrażeń regularnych dla wszystkich dopasowań, które porównują tekst.
Użycie
await browser.url('https://webdriver.io/')
const elem = await $('.container')
await expect(elem).toHaveText(/node\.js/i)
await expect(elem).toHaveText([/node\.js/i, 'Get Started'])
await expect(browser).toHaveTitle(/webdriverio/i)
await expect(browser).toHaveUrl(/webdriver\.io/)
await expect(elem).toHaveElementClass(/Container/i)
Domyślne dopasowania
Oprócz dopasowań expect-webdriverio
możesz używać wbudowanych asercji Jest expect lub expect/expectAsync dla Jasmine.
Asymetryczne dopasowania
WebdriverIO obsługuje użycie asymetrycznych dopasowań wszędzie tam, gdzie porównujesz wartości tekstowe, np.:
await expect(browser).toHaveTitle(expect.stringContaining('some title'))
lub
await expect(browser).toHaveTitle(expect.not.stringContaining('some title'))
TypeScript
Jeśli używasz WDIO Testrunner, wszystko zostanie automatycznie skonfigurowane. Po prostu postępuj zgodnie z instrukcją konfiguracji z dokumentacji. Jednak jeśli uruchamiasz WebdriverIO z innym testrunnerem lub w prostym skrypcie Node.js, musisz dodać expect-webdriverio
do types
w tsconfig.json
.
"expect-webdriverio"
dla wszystkich poza użytkownikami Jasmine/Jest."expect-webdriverio/jasmine"
dla Jasmine"expect-webdriverio/jest"
dla Jest
JavaScript (VSCode)
Wymagane jest utworzenie jsconfig.json
w głównym katalogu projektu i odniesienie do definicji typów, aby działało automatyczne uzupełnianie w czystym js.
{
"include": [
"**/*.js",
"**/*.json",
"node_modules/expect-webdriverio"
]
}
Dodawanie własnych dopasowań
Podobnie jak expect-webdriverio
rozszerza dopasowania Jasmine/Jest, możliwe jest dodanie niestandardowych dopasowań.
- Jasmine: sprawdź dokumentację custom matchers
- Dla pozostałych: sprawdź expect.extend w Jest
Niestandardowe dopasowania powinny być dodane w hooku before
wdio
// wdio.conf.js
{
async before () {
const { addCustomMatchers } = await import('./myMatchers')
addCustomMatchers()
}
}
// myMatchers.js - przykład dla Jest
export function addCustomMatchers () {
if (global.expect.expect !== undefined) { // Tymczasowe obejście. Zobacz https://github.com/webdriverio/expect-webdriverio/issues/835
global.expect = global.expect.expect;
}
expect.extend({
myMatcher (actual, expected) {
return { pass: actual === expected, message: () => 'some message' }
}
})
}