В e2e-тестировании визуальное состояние страницы в момент ошибки часто является ключевым источником информации. Скриншот фиксирует реальное состояние DOM, верстки, сообщений об ошибках, модальных окон и других элементов, которые могут исчезнуть при повторном запуске теста. В Protractor механизм снятия скриншотов тесно связан с жизненным циклом тестов и используемым тест-раннером (чаще всего Jasmine или Mocha).
Скриншоты при ошибках решают несколько задач одновременно:
Protractor предоставляет низкоуровневый метод для получения скриншота через WebDriver:
browser.takeScreenshot()
Метод возвращает Promise, который резолвится строкой в формате Base64. Для сохранения изображения требуется самостоятельно записать данные в файл.
Пример минимальной реализации:
const fs = require('fs');
const path = require('path');
browser.takeScreenshot().then(function (png) {
const stream = fs.createWriteStream(
path.join(__dirname, 'screenshot.png')
);
stream.write(Buffer.from(png, 'base64'));
stream.end();
});
Этот подход используется как основа для всех более сложных сценариев.
В Jasmine основной точкой расширения является
jasmine.getEnv().addReporter. Через кастомный репортер
можно отлавливать момент падения теста и инициировать снятие
скриншота.
Пример репортера, сохраняющего скриншот при ошибке:
const fs = require('fs');
const path = require('path');
const screenshotReporter = {
specDone: function (result) {
if (result.status === 'failed') {
browser.takeScreenshot().then(function (png) {
const fileName = `${result.fullName.replace(/\s+/g, '_')}.png`;
const filePath = path.join('screenshots', fileName);
fs.writeFileSync(
filePath,
Buffer.from(png, 'base64')
);
});
}
}
};
jasmine.getEnv().addReporter(screenshotReporter);
Ключевые моменты:
specDone, вызываемый после завершения
спецификации;failed гарантирует, что скриншот создаётся
только при ошибке;Protractor работает поверх WebDriverJS, поэтому все операции асинхронны. При падении теста важно, чтобы браузер ещё был доступен и не начал завершение сессии.
Для стабильной работы:
afterAll;restartBrowserBetweenTests;async/await необходимо явно ожидать
takeScreenshot.Пример с async/await:
specDone: async function (result) {
if (result.status === 'failed') {
const png = await browser.takeScreenshot();
fs.writeFileSync(
`screenshots/${Date.now()}.png`,
Buffer.from(png, 'base64')
);
}
}
afterEachИногда ошибка происходит не внутри теста, а в afterEach
(например, при очистке состояния). В таком случае specDone
может не отработать корректно.
Решение — использование process.on('unhandledRejection')
или явный перехват ошибок:
afterEach(async function () {
if (this.currentTest.state === 'failed') {
const png = await browser.takeScreenshot();
fs.writeFileSync(
`screenshots/${this.currentTest.title}.png`,
Buffer.from(png, 'base64')
);
}
});
Этот подход чаще используется с Mocha, но принцип аналогичен.
При большом количестве тестов хаотичное сохранение файлов быстро становится проблемой. Практика показывает эффективность следующей структуры:
screenshots/
├── chrome/
│ ├── login/
│ └── profile/
├── firefox/
└── edge/
Для этого в имени или пути используются:
browser.capabilities.get('browserName');Пример получения имени браузера:
browser.getCapabilities().then(function (caps) {
const browserName = caps.get('browserName');
});
onPrepareЧасто логика скриншотов выносится в onPrepare
конфигурационного файла Protractor, чтобы она применялась ко всем тестам
централизованно.
Пример в protractor.conf.js:
onPrepare: function () {
const fs = require('fs');
const path = require('path');
jasmine.getEnv().addReporter({
specDone: function (result) {
if (result.failedExpectations.length > 0) {
browser.takeScreenshot().then(function (png) {
const filePath = path.join(
'screenshots',
`${result.id}.png`
);
fs.writeFileSync(
filePath,
Buffer.from(png, 'base64')
);
});
}
}
});
}
Такой подход обеспечивает единообразие поведения без дублирования кода в тестах.
Популярные репортеры (Allure, Protractor HTML Reporter, Jasmine2 HTML Reporter) поддерживают автоматическое прикрепление скриншотов к отчётам.
Пример для Allure:
const allure = require('allure-commandline');
browser.takeScreenshot().then(function (png) {
allure.addAttachment(
'Screenshot',
Buffer.from(png, 'base64'),
'image/png'
);
});
В этом случае физическое сохранение файла может быть необязательным, так как артефакт хранится внутри отчёта.
browser.takeScreenshot() всегда делает скриншот видимой
области браузера. Для полноэкранных скриншотов требуется:
Частичные скриншоты (конкретного элемента) реализуются через WebDriver API:
const element = $('#error-message');
browser.takeScreenshot().then(function (png) {
// далее — обрезка изображения через стороннюю библиотеку
});
Сам Protractor не предоставляет встроенной функции для обрезки,
поэтому используются sharp, jimp или
аналогичные инструменты.
Для минимизации проблем рекомендуется:
В CI скриншоты обычно сохраняются как build artifacts. Для этого путь к ним должен быть предсказуемым и относительным.
Пример для Jenkins:
Archive Artifacts: screenshots/**/*.png
Это позволяет анализировать падения без локального воспроизведения тестов.
В крупных проектах логика скриншотов часто инкапсулируется в отдельный модуль:
module.exports.takeOnFailure = async function (testName) {
const png = await browser.takeScreenshot();
fs.writeFileSync(
`screenshots/${testName}.png`,
Buffer.from(png, 'base64')
);
};
Далее этот модуль используется в репортерах, хуках или кастомных ассертах, обеспечивая единый стандарт диагностики ошибок во всём тестовом наборе.