Тестування інтерфейсу, який щоразу змінюється
Переклад з англійського оригіналу.
Згенерована сторінка результатів пошуку рейсів може загубити ціну, коли змінюється її компонування. Я хотів мати перевірку, яка це помітить.
Створив uivet для перевірки інтерфейсів, які створює генератор. Той самий промпт може дати різне компонування, тому хотів перевіряти, чи результат досі містить надану інформацію і чи є на відображеній сторінці проблеми, які можна виявити.
Хотів, щоб перевірка переживала зміну компонування. Порівняння пікселів показало б, що сторінка змінилася, але мені також потрібно було знати, чи вона досі показує вхідні дані.
Перевіряти результат кілька разів
Сценарій uivet містить промпт, вхідні дані й кількість запусків. Кожен згенерований HTML-документ відображається в Chromium через Playwright. Тестова обв’язка перевіряє збереження даних, доступність, евристики компонування й помилки консолі, а потім об’єднує результати.
Вхідні дані дають перевірці конкретні значення для пошуку. Якщо дані рейсу містять ціну $1,248, сторінка має її зберегти. Гарний вигляд не компенсує її відсутності.
Реалізацію можна читати поруч зі статтею: перевірки, запуск та офлайн-конфігурація. Посилання ведуть на версію, використану нижче.
Кілька результатів допомагають побачити мінливість, але три успішні приклади не гарантують успіху четвертого. Звіт свідчить про ті сторінки, які справді запускалися.
У закріпленій версії проблеми компонування й консолі потрапляють у звіт без окремих правил зупинки. Додаткова модель-суддя за замовчуванням лише радить.
Запустити без моделі
Офлайн-демо відтворює збережений HTML і вимикає модель-суддю. Ключ API не потрібен, викликів моделі немає. У свіжій копії репозиторію, зі встановленим Bun:
git clone https://github.com/MaryanPrydatko/uivet.git
cd uivet
git checkout 66c67076e841bbb78128804b06b936ab3d2b9605
bun install --frozen-lockfile
bunx playwright install chromium
bun run demo:offline --out results/offline
Відкрийте results/offline/report.html, щоб переглянути результати. Встановлення браузера потребує завантаження; «офлайн» означає відтворення фікстур без генерації нових інтерфейсів чи звернення до моделі.
У версії, перевіреній для цієї статті, кожен із трьох сценаріїв запустився тричі, і демо завершилося успішно. Старіший запис у README показує збій через відсутню ціну. Поточні фікстури самі його не відтворюють, тому ось явний спосіб перевірити цей випадок.
Прибрати ціну й очікувати помилку
Збережіть цей код як check-missing-price.ts у свіжій робочій копії, а потім запустіть bun check-missing-price.ts. Він прибирає ціну з третьої фікстури рейсів, запускає перевірки й відновлює оригінальну фікстуру в блоці finally. Перевіряє і код завершення, і відсутнє значення у звіті, тож стороння помилка запуску не може зійти за виявлення бага.
import assert from 'node:assert/strict';
const path = 'examples/fixtures/flight-results/2.html';
const original = await Bun.file(path).text();
const output = `results/missing-price-${crypto.randomUUID()}`;
assert.ok(original.includes('$1,248'));
try {
await Bun.write(path, original.replaceAll('$1,248', ''));
const run = Bun.spawn([
'bun', 'run', 'demo:offline', '--out', output,
], { stdout: 'inherit', stderr: 'inherit' });
assert.equal(await run.exited, 1);
const report = await Bun.file(`${output}/results.json`).json();
const flight = report.scenarios.find(s => s.id === 'flight-results');
assert.ok(flight.runs[2].fidelity.missing.includes('$1,248'));
console.log(`The missing price failed the gate. Report: ${output}/report.html`);
} finally {
await Bun.write(path, original);
}
Вкладена команда демо має завершитися з кодом 1. Сам скрипт перевірки має завершитися успішно й вивести «The missing price failed the gate.» Кожен запуск використовує новий каталог у results/, шлях до якого друкує скрипт. Тому старий звіт не може випадково задовольнити перевірку. Запускайте цей експеримент окремо, щоб інший тест не читав фікстуру, поки вона тимчасово змінена.
Скрипт встановлює конкретний збій: фікстура втратила ціну, і перевірка збереження даних це виявила. Наступний експеримент показує, чого та сама перевірка може не помітити.
Поміняти ціни місцями й побачити успіх
Перевірка збереження даних обходить дані сценарію, збирає кінцеві рядкові й числові значення, нормалізує відображений текст і шукає кожне значення. Це робить її незалежною від структури згенерованого компонента. Не треба передбачати, які класи чи елементи обере модель.
Компроміс у тому, що вона вимірює наявність, а не зв’язки. Ось навмисно неправильний результат, який усе одно отримує повну оцінку збереження даних. Збережіть як check-fidelity-limit.ts у тій самій закріпленій версії й запустіть bun check-fidelity-limit.ts:
import assert from 'node:assert/strict';
import { computeFidelity } from './src/checks.ts';
const data = {
flights: [
{ airline: 'SWISS', price: '$1,248' },
{ airline: 'United', price: '$986' },
],
};
const swappedPrices = 'SWISS $986\nUnited $1,248';
const result = computeFidelity(data, swappedPrices);
assert.equal(result.rate, 1);
assert.deepEqual(result.missing, []);
console.log('Every value is present, but the prices belong to the wrong flights.');
Щоб виявити це, перевірка мала б знаходити кожен рядок рейсу й перевіряти його ціну саме в цьому рядку. Звідси ще одна вимога: стабільний спосіб знайти рейс у різних згенерованих компонуваннях. Перш ніж заявляти про перевірку зв’язку, я додав би це до контракту сценарію.
Підсумкова оцінка теж потребує уваги. uivet усереднює частки збережених значень між запусками. В експерименті з відсутньою ціною два запуски знаходять усі 25 очікуваних значень, а третій 24. Середнє становить приблизно 98,67% і показується як 99%. Це близько до ідеалу, але вимога полягала в збереженні кожного значення. Поріг 1 за замовчуванням відхиляє такий результат. Зниження порогу змінило б обіцянку зеленого статусу.
Зберігаю у звіті й окремі запуски. Середнє говорить менше, ніж знання того, яка генерація втратила яке значення. Випадковий збій моделі може бути для користувача важливішим, ніж підказує її середня оцінка.
Вирішити, які спостереження мають зупиняти запуск
Звіт і код завершення відповідають на різні питання. У закріпленій версії правила такі:
| Спостереження | Що відбувається |
|---|---|
| Відсутні очікувані значення | Перевірка збереження даних може зупинити запуск. Поріг за замовчуванням вимагає кожного значення. |
| Критичні порушення доступності | Перевірка доступності може зупинити запуск. Серйозні порушення теж потрапляють у звіт, але не входять до цього абсолютного правила. |
| Оцінка й узгодженість моделі-судді | Рекомендаційні, доки примусове застосування не ввімкнено. В офлайн-прикладі суддю вимкнено. |
| Проблеми компонування й консолі | Потрапляють у звіт без власних незалежних правил зупинки. |
Мій успішний офлайн-запуск усе одно повідомив про серйозні проблеми доступності. «PASS» означало відповідність налаштованим правилам, а не доступність сторінки чи готовність до публікації.
Збережена базова версія додає ще одне порівняння. Runner виявляє зниження частки збережених даних і появу нових ідентифікаторів правил доступності, навіть якщо запуск залишається вище абсолютних порогів. Погіршення оцінки судді є рекомендаційним, доки примусове застосування не ввімкнено. Оновлення базової версії приймає нову точку порівняння, тому я переглядав би цю зміну разом із реалізацією.
Прототип відображає сторінку при 1280 × 800 і не охоплює мобільних компонувань. Зіставлення тексту орієнтоване на англійську й чутливе до форматування. Приклад із переставленими цінами показує, чому навіть ідеальна оцінка наявності потребує конкретнішої перевірки перед схваленням інтерфейсу.
Для нового сценарію я почав би з результату, який має пройти, додав відомий збій і перевірив, яка частина звіту та правил завершення його ловить. Потім спробував би неправильний результат зі збереженими очевидними значеннями, як із цінами вище. Тоді можна вирішити, чи посилити перевірку, чи додати іншу для поведінки, яку вона пропускає.