Перейти к основному содержимому

Привет, Testplane 9!

· 9 мин. чтения

Встречайте Testplane 9: в этой версии заметно улучшились визуальные проверки, стала удобнее работа с глобальными объектами и появилось несколько полезных улучшений во всем фреймворке.

Testplane 9

Переход на Testplane 9

Руководство по миграции находится здесь. Ниже — обзор новых возможностей.

Полностью новый assertView

Главное изменение в Testplane 9 — полностью переработанная логика работы assertView.

Она лучше справляется с реальными интерфейсами: длинными страницами, скроллируемыми контейнерами, sticky-заголовками, fixed-оверлеями, шторками, модальными окнами, псевдоэлементами, тенями, обводками и другими случаями, из-за которых визуальные проверки часто становятся нестабильными.

Длинные скриншоты и контейнеры со скроллом

assertView теперь гораздо лучше работает с элементами, которые не помещаются в один вьюпорт.

Команда может автоматически определить, какой элемент нужно скроллить, найти неподвижные элементы и корректно обработать элементы с нестабильной высотой.

Код теста

Страница для теста

Testplane:

it("should capture a screenshot with assertView", async ({ browser }) => {
await browser.url("file:///page.html");

await browser.assertView("visual-engine-comparison", "[data-testid=list-container]");
});

Playwright:

test("should capture a scrollable drawer list with toHaveScreenshot", async ({ page }) => {
await page.goto("file:///page.html");

await expect(page.locator("[data-testid=list-container]")).toHaveScreenshot(
"visual-engine-comparison.png",
);
});
Скриншот скроллируемого контейнера в Testplane v9

Избегание мешающих элементов

В Testplane v9 assertView умеет скроллить элемент так, чтобы в итоговый скриншот не попадали наложения и нежелательные элементы.

Код теста

Страница для теста

Testplane:

it("should capture a screenshot of review item", async ({ browser }) => {
await browser.url("file:///page.html");

await browser.$(".Review.Review_hasReactions.ReviewViewer-Review").scrollIntoView();

await browser.assertView(
"visual-engine-comparison",
".Review.Review_hasReactions.ReviewViewer-Review",
);
});

Playwright:

test("should capture a screenshot of review item", async ({ page }) => {
await page.goto("file:///page.html");

const el = await page.locator(".Review.Review_hasReactions.ReviewViewer-Review").first();

await el.scrollIntoViewIfNeeded();

await expect(el).toHaveScreenshot("review.png");
});
Testplane v9Testplane v8Playwright
Скриншот в Testplane v9Скриншот в Testplane v8Скриншот в Playwright

Sticky- и fixed-элементы

Новая механика assertView вычисляет безопасную область до и во время съемки, поэтому sticky-заголовки, fixed-футеры и оверлеи не попадают в итоговый скриншот.

Код теста

Testplane:

it("captures a card with fixed page chrome around it", async ({ browser }) => {
await browser.url("file:///page.html");

await browser.assertView("analytics-card", "[data-testid=analytics-card]");
});

Playwright:

test("captures a card with fixed page chrome around it", async ({ page }) => {
await page.goto("file:///page.html");

await expect(page.locator("[data-testid=analytics-card]")).toHaveScreenshot(
"analytics-card.png",
);
});
Testplane v9Testplane v8Playwright
Скриншот со sticky-элементами в Testplane v9Скриншот со sticky-элементами в Testplane v8Скриншот со sticky-элементами в Playwright

Элементы с нестабильной высотой и lazy-load-контентом

В Testplane 9 assertView умеет работать с контентом, который подгружается при скролле, и заранее прогружать всю нужную область.

Если даже после такой прогрузки высота элемента меняется в процессе снятия скриншота, assertView постарается сделать скриншот без видимых разрывов.

Код и сравнение скриншотов
it("should capture a full page screenshot with assertView", async ({browser}) => {
await browser.url("https://sp.yandex.ru/s7");

await browser.execute(() => {
document.querySelectorAll('.DialElement_root__CftHX h1').forEach(h1 => {
h1.style.display = 'none';
});
});

await browser.assertView("visual-engine-comparison", "#__next");
});

Примечание: изображения ниже сильно сжаты из-за большого исходного размера.

Кликните на изображение, чтобы открыть его в новой вкладке.

Testplane v9Testplane v8Playwright
Скриншот страницы с lazy-load-контентом в Testplane v9Скриншот страницы с lazy-load-контентом в Testplane v8Скриншот страницы с lazy-load-контентом в Playwright

Подавление hover-эффектов

В команде assertView появилась опция disableHover, которая подавляет hover-эффекты во время снятия скриншота.

По умолчанию она включается автоматически, когда для того, чтобы снять скриншот элемента целиком, нужен скролл.

Примечание: на скриншоте Testplane v8 прямоугольниками выделены области, где видны hover-эффекты. В Testplane v9 их нет.

Testplane v9Testplane v8Playwright
Скриншот с подавленными hover-эффектами в Testplane v9Скриншот с hover-эффектами в Testplane v8Скриншот с hover-эффектами в Playwright

Поддержка псевдоэлементов

Обновленный assertView умеет снимать псевдоэлементы, например ::before и ::after.

В Testplane v8 для этого приходилось писать кастомную команду, которая сначала рисовала прямоугольник примерно поверх элемента, а затем снимала скриншот этой области.

В Testplane v9 assertView работает с псевдоэлементами наравне с обычными элементами.

Testplane v9Testplane v8Playwright
Скриншот псевдоэлемента в Testplane v9Скриншот псевдоэлемента в Testplane v8TypeError: The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received undefined

Другие заметные улучшения

В Testplane 9 есть и другие изменения, которые делают визуальное тестирование удобнее и стабильнее:

  • Значение tolerance по умолчанию теперь 3.0, а не 2.3, как в Testplane v8. То есть по умолчанию проверки assertView стали немного менее строгими.
  • Поддержка симуляторов iOS 26+ с новым Safari.
  • Опция cropMargins для обрезки скриншотов. Она полезна для обрезки скроллбаров в окружениях, где их нельзя скрыть.
  • Улучшенная отладка скриншотов: подробный лог и пошаговые скриншоты.

Асинхронный конфиг и lifecycle-хуки

Конфиг Testplane теперь может быть асинхронной функцией. Это позволяет, например, загружать секреты, обращаться к внешним сервисам, готовить временные тестовые данные или вычислять настройки браузеров перед запуском.

import type { ConfigInput } from "testplane";

export default async (): Promise<ConfigInput> => {
const baseUrl = await getPreviewUrl();

return {
baseUrl,
browsers: {
chrome: {
desiredCapabilities: { browserName: "chrome" },
},
},
};
};

prepareEnvironment тоже может быть асинхронным:

import type { ConfigInput } from "testplane";

const config: ConfigInput = {
prepareEnvironment: async () => {
process.env.AUTH_TOKEN = await createTestToken();
},

browsers: {
chrome: {
desiredCapabilities: { browserName: "chrome" },
},
},
};

export default config;

Некоторые lifecycle-события теперь тоже умеют ждать асинхронные обработчики, включая CLI и NEW_BROWSER.

const fs = require("node:fs/promises");

module.exports = testplane => {
testplane.on(testplane.events.CLI, async cli => {
const environments = JSON.parse(await fs.readFile("./test-envs.json", "utf8"));

cli.option("--env <name>", `Environment: ${environments.join(", ")}`);
});
};

Testplane больше не засоряет глобальный namespace

Исторически тесты Testplane опирались на глобальные объекты вроде it, describe, beforeEach, afterEach, testplane.* и hermione.*. В Testplane 9 эти хелперы явно экспортируются из пакета и по умолчанию не добавляются в глобальное пространство, чтобы не конфликтовать с другими test runner'ами:

import { describe, it, beforeEach } from "testplane";

describe("profile", () => {
beforeEach(async ({ browser }) => {
await browser.url("/profile");
});

it("shows user name", async ({ browser }) => {
await expect(browser.$("[data-testid=user-name]")).toHaveText("Alice");
});
});

Но можно и вернуть всё как было: достаточно установить пакет @testplane/globals.

Удаление automationProtocol: "devtools" и изменения в браузерных настройках по умолчанию

Testplane 9 использует WebDriver как единственный automationProtocol. Старый режим automationProtocol: "devtools" удален, локальный запуск браузеров можно делать с помощью gridUrl: "local".

Примечание: CDP-протокол и все, что с ним связано, по-прежнему можно использовать. Это изменение касается только опции automationProtocol.

Прекращение поддержки Node 18 и 20

Testplane 9 необходима Node 22 или новее для работы (мы рекомендуем v22.12+).