Web Drivers
Web Drivers - это инструменты для взаимодействия с браузером.
- Интерфейс удаленного управления, который позволяет анализировать и управлять браузером
- Платформонезависимый и не зависит от языка
- Предоставляет набор интерфейсов для нахождения и управления элементами DOM
- Не имеет прямого отношения к тестированию
Selenium
Selenium — один из популярных фреймворков для тестирования. Поддерживается всеми основными платформами и на всех браузерах. Он позволяет автоматизировать тестирование, имитировать действия пользователей.
Использование:
import { Builder, By, Key, until } from 'selenium-webdriver'
describe('web driver', () => {
let driver
test('google first result', async () => {
// Создаём инстанс вебдрайвера
driver = new Builder().forBrowser('chrome').build()
// Выполняем переход на страницу
driver.get('https://www.google.com')
// Получаем элемент ввода
const input = driver.findElement(By.name('q'))
// Вызываем на элементе события нажатия клавиш (ввод в поиск и Enter)
await input.sendKeys('hello', Key.ENTER)
// Дожидаемся пока не появится элемент и получаем его
const firstResult = await driver.wait(until.elementLocated(By.css('h3')), 10000)
// Выводим содержимое элемента
console.log(await firstResult.getAttribute('textContent'))
}, 10000)
afterEach(() => {
driver.quit()
})
})
В асинхронных запросах промисы должны возвращаться из тестов, иначе тесты не дожидаются выполнение асинхронных операций. Либо нужно использовать async await
import { Builder, By, Key } from 'selenium-webdriver'
const URL = 'http://svelte3-todo.surge.sh/'
describe('web driver', () => {
let driver
// Создаём драйвер перед выполнением тестов
beforeAll(() => {
driver = new Builder()
.forBrowser('chrome')
.build()
})
// Тест добавления таска
test('add a task', async () => {
// Выполняем переход на страницу
driver.get(URL)
// Возвращаем промис из теста
return driver.findElement(By.className('js-todo-input')).sendKeys('Build App', Key.ENTER)
// Асинхнронные запросы оборачиваем в цепочку промиса
.then(() => driver.getPageSource())
.then((source) => {
expect(source.includes('Build App')).toBe(true)
})
}, 1000)
// Тест отметки таска как пройденного
test('mark a task complete', () => {
driver.get(URL)
return driver.findElement(By.className('js-todo-input')).sendKeys('Build App', Key.ENTER) // Создаём таск
// Перед изменением таска, проверяем что он не завершен, для этого проверяем класс
.then(() => driver.findElement(By.className('todo-item')).getAttribute('class')) // получаем класс, асинхронный запрос
.then((className) => {
// проверяем что класс не содержит 'done'
expect(className.includes('done')).toBe(false)
// Вызываем клик по задаче (отмечаем что она завершена)
// это асинхронная операция, поэтому возвращаем промис
return driver.findElement(By.css('label')).click()
})
// Снова получаем класс, асинхронный запрос, поэтому оборачиваем в цепочку then
.then(() => driver.findElement(By.className('todo-item')).getAttribute('class'))
.then((className) => {
// Проверяем имя класса
expect(className.includes('done')).toBe(true)
})
}, 1000)
test('delete a task', () => {
driver.get(URL)
// Перед удалением также создаем таск
return driver.findElement(By.className('js-todo-input')).sendKeys('Build App', Key.ENTER)
// Кликаем по кнопке удаления
.then(() => driver.findElement(By.className('delete-todo')).click())
// Получаем содержимое страницы
.then(() => driver.getPageSource())
.then((source) => {
// Проверяем что таск удалён
expect(source.includes('Build App')).toBe(false)
})
}, 1000)
afterAll(() => {
driver.quit()
})
})
Тоже самое с async await
:
import { Builder, By, Key } from 'selenium-webdriver'
const URL = 'http://svelte3-todo.surge.sh/'
describe('web driver', () => {
let driver
// Создаём драйвер перед выполнением тестов
beforeAll(() => {
driver = new Builder()
.forBrowser('chrome')
.build()
})
// Тест добавления таска
test('add a task', async () => {
// Выполняем переход на страницу
driver.get(URL)
// Создаём таск
await driver.findElement(By.className('js-todo-input')).sendKeys('Build App', Key.ENTER)
const source = await driver.getPageSource()
expect(source.includes('Build App')).toBe(true)
}, 1000)
// Тест отметки таска как пройденного
test('mark a task complete', async () => {
driver.get(URL)
// Создаём таск
await driver.findElement(By.className('js-todo-input')).sendKeys('Build App', Key.ENTER)
// Перед изменением таска, проверяем что он не завершен, для этого проверяем класс
const classNameBefore = await driver.findElement(By.className('todo-item')).getAttribute('class') // получаем класс
// проверяем что класс не содержит 'done'
expect(classNameBefore.includes('done')).toBe(false)
// Вызываем клик по задаче (отмечаем что она завершена)
await driver.findElement(By.css('label')).click()
// Снова получаем класс
const classNameAfter = await driver.findElement(By.className('todo-item')).getAttribute('class')
// Проверяем имя класса
expect(classNameAfter.includes('done')).toBe(true)
}, 1000)
test('delete a task', async () => {
driver.get(URL)
// Перед удалением также создаем таск
await driver.findElement(By.className('js-todo-input')).sendKeys('Build App', Key.ENTER)
// Кликаем по кнопке удаления
await driver.findElement(By.className('delete-todo')).click()
// Получаем содержимое страницы
const source = await driver.getPageSource()
// Проверяем что таск удалён
expect(source.includes('Build App')).toBe(false)
}, 1000)
afterAll(() => {
driver.quit()
})
})
Cypress
Cypress — это e2e фреймворк для тестирования на JS, имеет свой тест-раннер, поддерживает множество языков.
Компонентное тестирование:
import * as React from 'react'
import { mount } from '@cypress/react'
import Button from './Button.jsx'
it('Button', () => {
// Рендерим кнопку
mount(<Button>Text button</Button>)
// Кликаем по кнопке
cy.get('button').contains('Test button').click()
})
// Тестируем завершения таска
it('complete todo', () => {
// Отрываем страницу
cy.visit('/')
// Находим элемент ввода, имитируем ввод имени таска и нажатие Enter
cy.get('.new-todo').type('write tests{enter}')
// Отмечаем выполнение таска
cy.contains('.todo-list li', 'write tests').find('.toggle').check()
// Проверяем наличие класса
cy.contains('.todo-list li', 'write tests').should('have.class', 'completed')
// При установленном плагине cypress-plugin-snapshots можно создавать скриншоты
cy.get('.todoapp').toMatchImageSnapshot({
imageConfig: {
threshold: 0.001,
},
})
})
// Тест добавления таска
it('adds todos', () => {
// Открываем страницу
cy.visit('/')
// Создаём два таска
cy.get('.new-todo')
.type('write E2E tests{enter}')
.type('add API tests as needed{enter}')
// Проверяем что запросы были отправлены
cy.request('/todos')
.its('body')
.should('have.length', 2)
.and((items) => {
// ...
})
})
Playwright
Playwright — библиотека от Microsoft, так же поддерживает множество языков. Не имеет своего тестраннера.
Пример использования:
// Подключаем библиотеку
import playwright from 'playwright';
(async () => {
// Тестируем на разных браузеров в цикле
for (const browserType of ['chromium', 'firefox', 'webkit']) {
// Запускаем браузер, получаем инстанс браузера
const browser = await playwright[browserType].launch()
// Получаем контекст браузера
const context = await browser.newContext()
// Получаем инстанс страницы
const page = await context.newPage()
// Открываем страницу
await page.goto('https://mail.ru')
// Вызываем событие
await page.click('[data-testid="enter-password"]')
// Создаем скриншот
await page.screenshot({ path: `mail-${browserType}.png` })
// Закрываем браузер
await browser.close()
}
})()
Имитация другого устройства:
import { webkit, devices } from 'playwright'
const iPhone11 = devices['iPhone 11 Pro']
describe(() => {
test('Main test', async () => {
// Запускаем браузер, получаем инстанс браузера
const browser = await webkit.launch()
// Получаем контекст устройства, задаём свои настройки
const context = await browser.newContext({
...iPhone11,
locale: 'en-US',
geolocation: { longitude: 12.492507, latitude: 41.889938 },
permissions: ['geolocation'],
})
// Получаем инстанс страницы
const page = await context.newPage()
// Открываем страницу
await page.goto('https://maps.google.com')
// Вызываем событие
await page.click('text="Your location"')
// Ждём выполнение запроса
await page.waitForRequest(/.*preview\/pwa/)
// Создаём скриншот
await page.screeshot({ path: 'iphone-11.png' })
// Закрываем браузер
await browser.close()
})
})
Puppeteer
Puppeteer — библиотека с упором на chrome. Синтаксис очень похож на playwright:
// Подключаем библиотеку
import puppeteer from 'puppeteer'
describe(() => {
test('Main test', async () => {
// Запускаем браузер и получаем инстанс браузера
const browser = await puppeteer.launch()
// Получаем инстанс страницы
const page = await browser.newPage()
// Открываем страницу
await page.goto('https://mail.ru')
// Вызываем событие
await page.click('[data-testid="enter-password"]')
// Создаем скриншот
await page.screenshot({ path: 'example.png' })
// Закрываем браузер
await browser.close()
})
})
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.