07 · Інструменти автоматизації
CodeceptJS: актор, хелпери і сценарний стиль
Зміст
Ви відкриваєте чужий репозиторій і бачите тест без , без обʼєкта сторінки і без жодного await:
Scenario('гість не бачить кабінет', ({ I }) => {
I.amOnPage('/');
I.dontSee('Кабінет', 'nav');
});
Перше враження — «ще один синтаксис для тих самих кліків». Насправді це інший рівень абстракції: сценарій каже, що робить користувач, а чим це виконується, вирішує конфігурація. Той самий текст може поїхати в браузер через Playwright, через WebDriver-протокол або в мобільний застосунок через Appium — і саме ця розвʼязка є причиною, чому інструмент узагалі існує.
Практичних інтересів тут два. Перший — читати такі репозиторії: не розуміючи, хто саме відповідає за крок, ви півгодини шукаєте відповідь у доці Playwright, поки причина живе на рівні надбудови. Другий — питання «коли доречний CodeceptJS»: сильна відповідь тут про рівень абстракції та його ціну, а не про список фіч. Три розібрані в главі «Ландшафт інструментів»; ця глава — про те, як надбудова над ними влаштована зсередини.
Актор I: делегування замість виклику
Тести пишуться як лінійний сценарій дій користувача, і кожен тест — функція Scenario з переданим у неї обʼєктом I.
I — не глобальна змінна для скорочення запису. Дока називає його (actor): це абстракція користувача-тестувальника і водночас до ввімкнених хелперів. Усі команди тесту делегуються бекендам — сам тест дій не виконує.
Звідси головна межа відповідальності: I.click('Увійти') не є ні кліком, ні браузера. Це запис у чергу, який хелпер перекладе на свій протокол. Тому фраза «у CodeceptJS не працює X» зазвичай означає «хелпер, який у нас увімкнений, не вміє X», і перше, що варто відкрити при розслідуванні, — не сторінку про сценарії, а сторінку конкретного хелпера.
Чому тест виглядає синхронним
await у прикладі вище немає не тому, що дії синхронні. Усі дії загорнуті в глобальний ланцюг і зчеплені між собою, тож звичайні команди ставляться в чергу автоматично.
Правило, де ця автоматика закінчується, дока формулює жорстко: await обовʼязковий для команд, що починаються з grab, і для викликів імпортованих функцій та методів . Звичайні дії (I.click(), I.fillField(), I.see()) його не потребують. Логіка проста: черга покриває дії актора, а не ваш власний код і не значення, які тест забирає зі сторінки.
Два наслідки, які варто знати одразу:
- Очікування вбудоване в дії. Інструмент сам чекає на елемент перед кліком, заповненням і більшістю інших взаємодій, тож явні очікування потрібні рідко.
- Невдалий крок повторюється автоматично. Це на рівні кроку — рівень нижче за ретрай тесту, і плутати їх у розмові про не варто.
Для чутливих значень є власна обгортка: I.fillField('password', secret('123456')) не світить значення в логу кроків.
Хелпери: один сценарій, різні бекенди
Хелпер (helper) — це бекенд, який насправді виконує кроки. Актор називає дію, а яким протоколом і яким браузером вона зробиться, вирішує підключений хелпер.
Бекенди дока називає поіменно, і вони перекривають різні моделі керування браузером одразу: Playwright (власний протокол інструмента), WebDriver (wire-протокол W3C), Puppeteer (протокол DevTools), Appium (мобільні застосунки).
Вибір робиться в конфігурації, і два найважливіші налаштування дока називає прямо: який хелпер (тобто який рушій виконує) і базовий URL застосунку. Хелпери вмикаються й конфігуруються секцією helpers:
// codecept.conf.ts — той самий набір сценаріїв, інший виконавець
helpers: {
Playwright: { url: 'https://mysite.com', browser: 'firefox' }
}
І тут головний компроміс, через який цю главу взагалі варто читати уважно. Хелпери мають спільну API, тож перемкнути бекенд легко. Але дока сама ставить межу: через відмінності й обмеження бекендів вони не гарантовано сумісні між собою — і контрприклад наводить власний: заголовки запиту можна ставити в Playwright і Puppeteer, а у WebDriver ні. Порада сформульована як вибір, а не як обіцянка універсальності: узяти один хелпер, а міграцію робити, якщо зміняться вимоги.
Різниця між «легко мігрувати» і «працює однаково» — це різниця між правдою і тим, що зазвичай обіцяють на дошці. Архітектурні причини цих відмінностей — у главі «Playwright: архітектура, браузери і контексти».
Семантичні локатори: рядок, який читається як сторінка
У прикладі на початку глави замість селектора стоїть рядок 'Кабінет'. Це не спрощення для документації, а один із двох штатних видів локаторів.
- Локатор із явною стратегією — обʼєкт, ключ якого прямо називає стратегію пошуку:
{ css: 'button' },{ xpath: '//td[1]' },{ id: 'email' }, а для ролі —{ role: 'button', name: 'Submit' }, де другий ключ уточнює доступне імʼя. Стратегія відома наперед, тож хелпер робить рівно один запит. Дока називає такий локатор «строгим» — і тут легко наштовхнутися на омонім: строгість тут про однозначність стратегії пошуку, а не про кількість знайдених елементів. Строгий режим локаторів Playwright — зовсім інше поняття, і воно розібране в главі «Playwright: локатори і дії з елементами». - Семантичний локатор — звичайний рядок на кшталт
'Sign In'або'Email'. Інструмент зіставляє його з підписами, текстом кнопок, плейсхолдерами йaria-*атрибутами — так, як сторінку читає користувач.
Обидва види ідіоматичні, але найсильнішим патерном дока називає семантичний локатор у контексті — тобто з другим аргументом, який обмежує зону пошуку (I.click('Зберегти', '.modal')). Мотив названо прямо: дублікати підписів деінде на сторінці більше не дають нестабільних збігів. Це рекомендований дефолт для стабільних сценаріїв, а не скорочення для прототипу.
Ціна семантичного пошуку теж названа чесно: він робить кілька запитів замість одного, але кожен запит дешевий, а контекст різко звужує простір пошуку. Для fillField і схожих дій порядок спроб такий:
Ланцюг варто прочитати уважно один раз, бо він знімає два непорозуміння. Перше: рядок, який ви вважали текстом підпису, на останньому кроці трактується як CSS-селектор — тож дивна помилка на локаторі 'input' має цілком логічне . Друге: ролевий локатор резолвиться через дерево доступності (accessibility tree), тобто напряму залежить від семантики розмітки — тієї самої, про яку йдеться в главі «Семантичний HTML і доступність».
CSS дока при цьому не забороняє: вона називає його найшвидшим типом локатора, який більшість фронтендерів читає без запинки, і дає сценарій, де швидкість вирішує, — гарячий цикл із тисячами викликів локатора.
Кастомні кроки і page object: розширюють актора, а не базовий клас
Патерн page object тут той самий, що всюди: обʼєкт обгортає сторінку або її фрагмент застосунковим API, щоб тест не длубався в HTML, і в лишається рівно одне місце, яке знає структуру розмітки конкретної частини сторінки. Змінюється не патерн, а механіка доставки — і вона в цьому інструменті інша, ніж у решті.
По-перше, спільні кроки розширюють актора. Під час ініціалізації проєкт пропонує створити файл кастомних кроків, і саме через нього до I додаються власні дії. У класичному ту саму роль зазвичай грає базовий клас тесту або ; тут спільна поведінка живе на акторі.
По-друге, page object не імпортують, а інжектять за іменем. Обʼєкти оголошуються в секції include конфігурації, а отримати їх можна параметром сценарію або глобальним викликом inject():
// codecept.conf.ts
include: { I: './steps_file.js', loginPage: './pages/Login.js' }
// tests/login_test.ts
Scenario('вхід із валідними даними', async ({ I, loginPage }) => {
await loginPage.signIn('user@example.com', secret('123456'));
I.see('Кабінет', 'nav');
});
Механізм — (dependency injection): клас експортується як є, а екземпляр створює . Виклик inject() на початку файла повертає ледачий проксі, тож I та інші обʼєкти резолвляться в момент виклику — деструктуризувати їх до означення класу безпечно. Звідси й await у прикладі: метод page object — це вже ваш код, а не дія актора, і черга його не покриває.
По-третє, технічно тотожні page object: різниця лише концептуальна — фрагмент описує автономну частину сторінки (модалка, віджет, компонент). Імена і page object, і фрагментів оголошуються в тій самій секції include.
Варто розвести два «актори», які легко змішати. У патерні Screenplay актор — один із пʼяти будівельних блоків (актори, здібності, інтеракції, задачі, питання), він представляє людей і зовнішні системи, а сценарій може мати кількох акторів: один готує дані, інший працює через UI. Тут I — насамперед проксі до хелперів, тобто одиниця абстракції інша: не «задача актора», а «крок сценарію». Питання «page object чи Screenplay» — територія розділу про стратегію автоматизації, і відповідь там не «краще/гірше», а «яка одиниця абстракції».
Плагіни: ретрай кроку, логін раз на прогін, артефакти
Плагіни тут постачаються разом із фреймворком, тож увімкнути потрібне — питання конфігурації, а не пошуку сторонньої залежності. Це важлива деталь для читання чужого репозиторію: значна частина поведінки, яку ви сприймаєте як «магію фреймворку», стоїть рядком у секції plugins.
Що варто знати поіменно:
retryFailedStep— повторює кожен невдалий крок у тесті. Разом із вбудованим ретраєм кроку це другий важіль на тому самому рівні; таксономія причин й політика ретраїв — територія розділу про стратегію автоматизації, а механіка засобами інструмента — глави «Боротьба з флаком засобами інструмента».auth— логінить користувача в першому тесті й перевикористовує сесію далі: кукі зберігаються в памʼять або файл, а якщо сесія протухає, плагін логіниться знову сам. Якщо у вашому конспекті стоїтьautoLogin— у чинному реєстрі плагінів такого рядка немає; функцію несеauth.- Артефакти падіння — окремі плагіни, а не вбудована поведінка: при падінні, збір інформації зі сторінки після кожного невдалого тесту (дока прямо радить вмикати його, якщо ви ганяєте тести на CI й вам треба дебажити падіння) і запис відео у WebM через screencast API Playwright.
- Звіт JUnit XML — теж окремий плагін: після прогону він генерує XML-звіт, сумісний із JUnit, — саме його зазвичай і читає CI; репортинг детальніше — у главі «Playwright: дебаг, trace viewer і репортинг».
- кроку ставиться глобально окремим плагіном. Сусідній плагін додає між діями — і його варто читати як маскування повільності застосунку, а не як лікування.
Порівняння з Playwright тут показове. Там живе у файлі, а за протуханням інструмент не стежить — видалити протухлий файл має автор тестів (глава «Playwright: автентифікація і повторне використання стану»). Тут ту саму роботу бере на себе плагін. Це не «краще»: автоматичне поновлення сесії зручне, поки ви не тестуєте саму поведінку протухлої сесії.
Окремо про аварійний вихід. Сценарний стиль — абстракція, і абстракції протікають; фреймворк це визнає сам, бо один із плагінів віддає в сценарій внутрішній обʼєкт хелпера (наприклад, page із Playwright). Тобто спуститися на рівень нижче можна штатно — і саме там закінчується «один сценарій для всіх бекендів»: код, який торкнувся внутрішнього обʼєкта, привʼязаний до конкретного хелпера. У поставці є ще й клас плагінів на AI: самовідновлення тестів, аналіз падінь і у форматі, розрахованому на AI-агента.
Коли CodeceptJS доречний
Відповідь на це питання прямо випливає з хелперів: інструмент доречний тоді, коли вам потрібен один читабельний сценарний шар над різними бекендами — наприклад, веб і мобільний застосунок в однаковому стилі, або команда, у якій сценарії читають не лише автоматизатори. Дока сама називає синтаксис BDD-подібним — але «BDD-подібний» тут про читабельність запису в JavaScript, а не про Gherkin: у цьому стилі сценарій лишається кодом, а не текстом фіч-файла, зіставленим зі (їхня механіка — у главі «BDD-інструменти: CucumberJS і Gherkin»).
Ціна цього шару — теж із хелперів, і назвати її треба самому, не чекаючи на уточнювальне питання:
- Спільна API уніфікує запис, а не поведінку: гарантованої сумісності бекендів немає.
- Між тестом і інструментом стоїть додатковий шар, і дебажити ви будете крізь нього — треба знати обидва рівні, а не один.
- Аварійний вихід до внутрішнього обʼєкта хелпера існує, і кожне його використання привʼязує сценарій до конкретного бекенда.
Дзеркальна ситуація, де інструмент нічого не додає: одна команда, один браузерний стек, тести пишуть інженери, які й так знають API інструмента напряму. Тоді читабельність сценарію купується лише зайвим рівнем непрямості — і рекомендація доки «взяти один хелпер» перетворюється на «взяти інструмент, який цим хелпером і є».
Типові помилки
Виглядає як «перемкнемо хелпер — і сюїта поїде на WebDriver». Насправді спільна API сумісності не гарантує, і дока дає власний контрприклад: заголовки запиту ставляться в Playwright і Puppeteer, а у WebDriver ні. Обіцянка інструмента — «легко мігрувати», а не «працює однаково».
Виглядає як «тут await не потрібен ніде». Насправді правило вужче й жорсткіше: grab-команди, імпортовані функції й методи page object чекають вручну. Черга покриває дії актора, а не ваш код.
Виглядає як флак локатора. Насправді семантичний рядок збігся з дублікатом підпису деінде на сторінці. Лікується другим аргументом (контекстом), а не втечею на CSS — рекомендований дефолт саме такий.
Виглядає як «строгий локатор — це однозначний збіг». Насправді строгість тут про явно названу стратегію пошуку, тобто про один запит замість ланцюга спроб. Кількість знайдених елементів — інше поняття й інший інструмент.
Виглядає як «ретраї ввімкнені, флак під контролем». Насправді retryFailedStep працює на рівні кроку, і це не те саме, що ретрай тесту: різні рівні ловлять різні причини, а розмова, у якій їх не розрізняють, зводиться до «просто перезапустимо».
Виглядає як «autoLogin зламався». Насправді в чинному реєстрі плагінів такої назви немає — потрібну функцію несе auth.
Виглядає як «page object не імпортується, значить не працює». Насправді його й не імпортують: обʼєкт оголошується в include і приходить у сценарій за іменем, а екземпляр створює контейнер впровадження залежностей.
Виглядає як «затримка між діями полагодила флак». Насправді вона його прикрила: плагін додає паузи, а не прибирає причину, чому застосунок не встигає.
Підсумок
- Актор нічого не виконує.
I— проксі до ввімкнених хелперів; хто робить крок насправді, вирішує конфігурація, і туди ж варто дивитися першим при розслідуванні. - Спільна API уніфікує запис, а не поведінку. Перемкнути бекенд легко, гарантованої сумісності бекендів немає — це формулювання самої доки, разом із її контрприкладом.
- Синхронний вигляд тесту — це глобальний промісний ланцюг. Межа автоматики названа поіменно:
grab, імпортовані функції й методи page object — зawait. - Семантичний локатор плюс контекст — рекомендований дефолт. Ціна в кількох запитах реальна, але контекст її знімає, а він же прибирає збіги з дублікатами підписів.
- Помітна частина поведінки живе в плагінах, і вони в поставці. Ретрай кроку, логін раз на прогін із автоматичним перелогіном, артефакти падіння і JUnit XML вмикаються конфігурацією.
Можливі питання
Питання тут конкретні — по механіці, яку не вигадаєш.
«Що таке I?» — питання-фільтр. Відповідь «обʼєкт із методами для дій» проходить за трієчку; точна відповідь: актор, тобто абстракція користувача-тестувальника і проксі до ввімкнених хелперів, який делегує всі команди бекенду. Інтервʼюер дивиться, чи розумієте ви, що між тестом і браузером є шар.
«Чому в тестах немає await?» — перевірка, чи ви читали доку, а не чужі приклади. Сильна відповідь: дії зчеплені в глобальний промісний ланцюг і ставляться в чергу автоматично, але правило має межу — grab-команди, імпортовані функції й методи page object треба чекати вручну.
«Чим це відрізняється від Playwright?» — тут очікують рівень абстракції, а не список фіч. Playwright сам є бекендом, і саме він може стояти хелпером під сценарієм; надбудова дає спільний сценарний запис поверх кількох бекендів і платить за це додатковим шаром. Назвіть межу самі: спільна API не означає гарантованої сумісності.
«Як тут роблять page object?» — питання на механіку. Патерн той самий, доставка інша: обʼєкт оголошується в include, приходить у сценарій за іменем через впровадження залежностей, а спільні кроки розширюють актора, а не базовий клас тесту. Згадка про inject() і про те, що методи page object вимагають await, читається як реальний досвід.
«Які плагіни вмикаєте на проєкті?» — практичне питання. Мінімум, який варто назвати: ретрай кроку, логін із сесії, скріншот і збір даних зі сторінки при падінні, JUnit XML для CI. Якщо назвете плагін затримки між діями — одразу додайте, що це маскування, інакше відповідь прочитають як звичку глушити симптоми.
«Коли обереш CodeceptJS?» — відповідь через хелпери: коли потрібен один читабельний сценарний шар над різними бекендами, наприклад веб і мобайл в одному стилі. І одразу назвіть ціну: додатковий рівень непрямості й відсутність гарантованої взаємозамінності бекендів. Якщо інструмент вам незнайомий, найкраща відповідь — назвати межу свого знання, а не вигадувати деталь архітектури.
Джерела
Актор I: делегування замість виклику
- CodeceptJS — Basics — тест як лінійний сценарій із обʼєктом
I;Iяк актор і проксі до ввімкнених хелперів; делегування команд бекендам; глобальний промісний ланцюг і жорстке правилоawaitдляgrabі методів page object; вбудоване очікування й автоматичний ретрай невдалого кроку;secret()для значень, які не мають потрапляти в лог.
Хелпери: один сценарій, різні бекенди
- CodeceptJS — Basics — делегування команд хелперу-бекенду; перелік бекендів і протоколів, які за ними стоять; спільна API без гарантії сумісності з контрприкладом про заголовки запиту; порада взяти один хелпер і мігрувати за зміни вимог; два найважливіші налаштування конфігурації.
- CodeceptJS — Configuration — секція
helpersяк місце, де хелпер задає рушій виконання разом із базовим URL.
Семантичні локатори: рядок, який читається як сторінка
- CodeceptJS — Locators — два види локаторів: обʼєкт із явно названою стратегією (один запит) і семантичний рядок, що зіставляється з підписами, текстом кнопок, плейсхолдерами й
aria-*; семантичний локатор у контексті як рекомендований дефолт і мотив «дублікати підписів більше не дають нестабільних збігів»; повний порядок розвʼязання локатора аж до трактування рядка як CSS-селектора; ціна в кількох запитах; CSS як найшвидший тип.
Кастомні кроки і page object: розширюють актора, а не базовий клас
- CodeceptJS — Page Objects — мотивація (спільні зони взаємодії, щоб не дублювати локатори й методи); впровадження залежностей замість імпортів,
inject()як ледачий проксі й автоматичне створення екземпляра контейнером; кастомні кроки як розширення актораI; фрагменти сторінки, технічно тотожні page object. - CodeceptJS — Configuration — секція
includeяк реєстр обʼєктів, що інжектяться в сценарій за іменем. - Martin Fowler — PageObject — сам патерн: обгортка сторінки або фрагмента застосунковим API замість роботи з HTML у тесті.
- Selenium — Page object models — наскрізний інваріант патерна: рівно одне місце в сюїті знає структуру HTML конкретної частини сторінки.
- Serenity/JS — Screenplay Pattern — актор у Screenplay: пʼять будівельних блоків, актор як людина або зовнішня система, один або кілька акторів у сценарії.
Плагіни: ретрай кроку, логін раз на прогін, артефакти
- CodeceptJS — Plugins — плагіни як частина поставки;
retryFailedStepяк ретрай кожного невдалого кроку; плагін логіну з перевикористанням сесії, збереженням кукі в памʼять або файл і автоматичним перелогіном при протуханні; артефакти падіння (скріншот, збір даних зі сторінки з прямою рекомендацією для CI, відео); звіт JUnit XML; таймаут кроку й затримка між діями; доступ до внутрішнього обʼєкта хелпера; плагіни на AI. - Playwright — Authentication — інша половина порівняння: інструмент не стежить за протуханням збереженого стану, видалити протухлий файл має автор тестів.
- CodeceptJS — Basics — сценарний шар над різними бекендами як призначення інструмента; BDD-подібний синтаксис; спільна API без гарантії сумісності; порада взяти один хелпер, а міграцію робити за зміни вимог.
- CodeceptJS — Configuration — хелпер і базовий URL як два налаштування, що визначають, чим виконується той самий набір сценаріїв.
- CodeceptJS — Plugins — штатний вихід до внутрішнього обʼєкта хелпера як межа переносності сценарію.
- CodeceptJS — Basics — спільна API без гарантованої сумісності з контрприкладом про заголовки запиту; правило
awaitдляgrabі методів page object; автоматичний ретрай кроку як рівень нижче за ретрай тесту. - CodeceptJS — Locators — контекст як засіб проти збігів із дублікатами підписів; «строгість» локатора як явно названа стратегія пошуку.
- CodeceptJS — Plugins —
retryFailedStepна рівні кроку; плагін логіну під назвоюauth; затримка між діями як окремий плагін. - CodeceptJS — Page Objects — page object приходить у сценарій через впровадження залежностей, а не через імпорт.
- CodeceptJS — Basics — актор як проксі до хелперів і делегування; спільна API без гарантії сумісності; промісний ланцюг і правило
await. - CodeceptJS — Locators — семантичний локатор у контексті як рекомендований дефолт і його ціна.
- CodeceptJS — Plugins — вбудовані плагіни: ретрай кроку, логін із перевикористанням сесії, артефакти падіння, JUnit XML.
- CodeceptJS — Configuration — конфігурація як місце, де задається хелпер, базовий URL і реєстр інжектованих обʼєктів.
- CodeceptJS — Basics — матеріал для відповідей про
I, делегування, промісний ланцюг і правилоawait, а також про доречність надбудови й межу спільної API. - CodeceptJS — Page Objects — механіка page object через впровадження залежностей і кастомні кроки як розширення актора.
- CodeceptJS — Plugins — перелік вбудованих плагінів, з якого складається відповідь про налаштування проєкту.
Що таке обʼєкт I у тесті на CodeceptJS?
Це (actor) — абстракція користувача-тестувальника й водночас до ввімкнених хелперів. Відповідь «обʼєкт із методами для дій» формально не бреше, але пропускає головне: сам актор нічого не робить. Рядок I.click('Увійти') — не клік і не звернення до API браузера, а запис у чергу, який потім перекладе на свій протокол той бекенд, що вказаний у конфігурації. Практичний наслідок один і дуже конкретний: між тестом і браузером стоїть шар, і при розслідуванні дивитися треба спершу на нього. Саме тому фраза «у CodeceptJS не працює така-то дія» майже завжди означає «наш хелпер її не вміє». Інтервʼюер цим питанням перевіряє, чи бачить кандидат цей шар узагалі, чи вважає I просто скороченням запису.
Чому в тестах на CodeceptJS майже ніде немає await? Це синхронний код?
Ні, код асинхронний — просто дії актора автоматично зчіплюються в один глобальний ланцюг, тож ставити їх у чергу вручну не потрібно. Тому I.amOnPage(), I.click(), I.fillField(), I.see() виконуються послідовно, без жодного await. Але автоматика має чітко названу межу: чекати вручну треба команди сімейства grab, виклики імпортованих функцій і методи обʼєктів сторінки. Логіка межі проста: у чергу потрапляють кроки самого актора, а от ваші функції й значення, витягнуті зі сторінки в змінну, фреймворк не чекає. Забутий await перед grab дає найнеприємніший клас багів: у змінній лежить проміс, порівнює його з рядком і падає з дивним повідомленням, а виглядає це як «локатор не спрацював».
Що таке хелпер і як він співвідноситься з актором?
Хелпер (helper) — це бекенд, який насправді виконує кроки сценарію. Розподіл ролей такий: актор називає дію застосунковими словами, а чим і яким протоколом вона зробиться, вирішує підключений хелпер. У поставці є хелпери під різні моделі керування: Playwright ходить власним протоколом, WebDriver — wire-протоколом W3C, Puppeteer — протоколом DevTools, Appium працює з мобільними застосунками. Один і той самий текст сценарію може поїхати будь-яким із них — саме ця розвʼязка і є причиною існування інструмента. Для роботи це означає інший маршрут читання доки: питання «як зробити X» адресується сторінці конкретного хелпера, а не сторінці про сценарії.
Де в проєкті задається, чим саме виконуються сценарії?
У конфігураційному файлі, у секції helpers — там хелпер і вмикається, і налаштовується. Два налаштування, від яких залежить найбільше, — це який саме хелпер увімкнений (тобто який рушій виконує кроки) і базовий URL застосунку. Виглядає це буквально як кілька рядків: назва хелпера, адреса застосунку, браузер. Практична цінність цього знання — швидка орієнтація в чужому репозиторії: перш ніж дивитися сам тест, варто відкрити конфіг і зʼясувати, хто виконавець, бо від цього залежить, у чиїй доці шукати відповідь на «чому цей крок так поводиться».
Які види локаторів є в CodeceptJS?
Два штатних. Перший — із явно названою стратегією: обʼєкт, ключ якого прямо каже, як шукати, наприклад { css: 'button' }, { xpath: '//td[1]' } чи { id: 'email' }; ролевий записується парою { role: 'button', name: 'Submit' }, у якій другий ключ задає доступне імʼя елемента. Оскільки стратегія відома наперед, хелпер робить рівно один запит до сторінки. Другий — семантичний локатор: звичайний рядок на кшталт 'Sign In' або 'Email', який інструмент зіставляє з підписами, текстом кнопок, плейсхолдерами й aria-* атрибутами, тобто читає сторінку приблизно так, як її читає людина. Обидва види ідіоматичні: це не пара «правильний» і «для прототипу».
Що таке семантичний локатор і чим він платить за читабельність?
Це рядок з тим, що користувач бачить на екрані, замість селектора: замість 'button[type=submit]' у тесті стоїть 'Увійти'. Плюс очевидний — сценарій читається як опис поведінки, і його розуміє людина, яка не пише тестів. Ціна теж названа прямо: замість одного запиту хелпер робить кілька, бо перебирає варіанти зіставлення по черзі. Кожен окремий запит дешевий, тому в звичайному сценарії різниця непомітна; критичною вона стає лише в гарячому циклі з тисячами звернень до локатора — там доречніше взяти CSS, який документація називає найшвидшим типом. Другий бік ціни серйозніший за швидкість: рядок може збігтися з дублікатом підпису деінде на сторінці, і саме звідси береться , який виглядає як «локатор нестабільний».
Тест на I.click('Зберегти') іноді клікає не туди. Причина і лікування?
Причина майже завжди в тому, що підпис «Зберегти» є на сторінці більше ніж в одному місці — наприклад, у відкритій модалці й у формі під нею, — і семантичний пошук натрапляє не на той елемент. Лікування рекомендоване документацією: додати другий аргумент, який обмежує зону пошуку, наприклад I.click('Зберегти', '.modal'). Це не костиль, а дефолтний патерн для стабільних сценаріїв: контекст одночасно знімає збіги з дублікатами й різко звужує простір пошуку, тобто гасить і ціну кількох запитів. Типова помилка на цьому місці — втекти на CSS-селектор і втратити читабельність, хоча проблема була не в семантичному локаторі, а у відсутності контексту. Сильна відповідь на співбесіді саме така: спершу контекст, і лише потім розмова про стратегію пошуку.
Як у CodeceptJS реалізують page object?
Сам патерн той самий, що будь-де: замість роботи з розміткою тест отримує застосункове API поверх сторінки чи її фрагмента, а знання про структуру HTML цієї частини лишається в одному-єдиному місці . Відрізняється механіка доставки. Обʼєкт не імпортують у файл тесту — його імʼя оголошують у секції include конфігурації, а в сценарій він приходить як параметр разом з I або через глобальний виклик inject(). Під цим лежить (dependency injection): клас експортується як є, а екземпляр створює фреймворку. Виклик inject() віддає ледачий проксі, тому деструктуризувати I та інші обʼєкти на початку файла, ще до означення класу, безпечно — резолвляться вони в момент виклику. Через це відсутність імпорту в чужому репозиторії — не ознака поломки: шукати треба конфіг, а не рядок import.
Чому метод обʼєкта сторінки треба чекати через await, а I.click() — ні?
Бо це код різної природи. I.click() — дія актора, вона автоматично додається у спільний промісний ланцюг фреймворку. Метод обʼєкта сторінки — ваша власна функція: усередині неї є дії актора, але сам виклик у чергу фреймворку не потрапляє, тому чекати його доводиться руками. Те саме правило накриває будь-яку імпортовану функцію й команди grab. Практичний симптом забутого await — сценарій рушає далі, не дочекавшись, поки метод доробить, і падіння приходить не там, де живе причина. Це один із небагатьох випадків, де знання правила напамʼять реально економить години.
Куди в CodeceptJS діваються спільні кроки — базовий клас тесту чи фікстура?
Ні того, ні того: спільні кроки розширюють актора. Під час ініціалізації проєкту фреймворк пропонує створити окремий файл кастомних кроків — саме там до I і дописують власні дії, скажімо I.loginAs('admin'). Тобто там, де у звичайному спільну поведінку тримають у базовому класі або у , тут вона живе на акторі й доступна в кожному сценарії без імпорту. Це варто знати заздалегідь, бо інакше кастомний крок у чужому репозиторії виглядає як недокументована фіча фреймворку: ви шукаєте його в доці, а він оголошений у файлі проєкту й підключений тим самим механізмом include.
Чи справді можна перевести сюїту з Playwright на WebDriver, змінивши хелпер у конфізі?
Перемкнути — легко, а от очікувати однакової поведінки не варто, і документація тут чесна: у хелперів спільна API, але через відмінності й обмеження самих бекендів гарантованої сумісності між ними немає. Власний контрприклад доки: заголовки запиту можна ставити через Playwright і Puppeteer, а через WebDriver — ні. Тобто обіцянка інструмента — «легко мігрувати», а не «працює однаково», і різниця між цими двома формулюваннями і є суть питання. Порада самої доки звучить як вибір, а не як гасло універсальності: узяти один хелпер, а міграцію робити тоді, коли справді зміняться вимоги. На співбесіді цю межу варто назвати самому — інакше відповідь читається як переказ маркетингу.
Тест падає з ElementNotFound на локаторі 'input'. Що відбулося?
Рядок пройшов увесь ланцюг зіставлення й на останньому кроці був витрактований як CSS-селектор — а на сторінці або немає жодного input, або він не той, на який ви розраховували. Порядок спроб для заповнення поля і схожих дій такий: спершу ARIA-роль через дерево доступності, потім локатор із явною стратегією, потім точний збіг з name, id разом із label[for], плейсхолдером або підписом навколо поля, далі той самий збіг частково плюс aria-label, aria-labelledby, title, потім атрибут name, і тільки в кінці — рядок як CSS-селектор; якщо не спрацювало нічого, летить ElementNotFound. Прочитати цей ланцюг один раз варто саме через такі випадки: слово, яке ви вважали текстом підпису, наприкінці стає селектором, і помилка перестає бути загадковою. Друга корисна деталь того самого ланцюга: ролевий локатор резолвиться через дерево доступності, тобто напряму залежить від семантики розмітки — зламали семантику у фронтенді, і локатор перестав знаходити елемент, хоча на екрані все на місці.
Документація називає локатор із явною стратегією «строгим». Це те саме, що strict mode у Playwright?
Ні, і це класичний омонім, на якому легко посковзнутися. Строгість у CodeceptJS — про однозначність стратегії пошуку: ви прямо сказали, шукати за CSS, XPath чи id, тому хелпер робить один запит замість ланцюга спроб. Строгий режим Playwright — про кількість знайдених елементів: якщо локатор дав більше одного збігу, дія падає замість того, щоб узяти перший. Це різні поняття з різних рівнів: перше про те, як шукати, друге про те, що робити з кількома результатами. Плутанина тут не безкоштовна: людина, яка вважає «строгий локатор» гарантією єдиного збігу, буде здивована, коли явно вказаний CSS-селектор спокійно знайде десять елементів.
Які плагіни ви вмикаєте на проєкті?
Ключова деталь, з якої варто почати: плагіни постачаються разом із фреймворком, тож питання не в тому, що поставити, а в тому, що ввімкнути в секції plugins. Розумний мінімум: невдалого кроку, плагін логіну з сесії, при падінні, збір інформації зі сторінки після невдалого тесту (документація прямо радить його для прогонів на CI, де падіння треба дебажити постфактум) і звіт у JUnit XML, бо цей формат читають і CI-системи, і TMS. Окремо існують запис відео у WebM через screencast API Playwright, глобальний кроку і між діями. Останню, якщо називаєте, одразу супроводжуйте оцінкою: вона маскує повільність застосунку, а не лікує її — інакше відповідь прочитають як звичку глушити симптоми. І практичний бонус цього знання: значна частина поведінки, яка в чужому репозиторії виглядає магією фреймворку, насправді стоїть одним рядком у конфізі.
Увімкнений retryFailedStep — чи можна вважати, що флак під контролем?
Ні, і це питання на розрізнення рівнів. retryFailedStep повторює кожен невдалий крок у тесті, тобто працює на рівні кроку — і на тому самому рівні працює вбудований автоматичний ретрай невдалої дії. Ретрай тесту — інший рівень: він переграє сценарій цілком. Різні рівні ловлять різні причини: повтор кроку врятує від елемента, що зʼявився на пів секунди пізніше, але нічого не зробить із протухлою сесією чи брудними даними, які зламали сценарій на середині. Розмова, у якій ці рівні не розрізняють, зазвичай зводиться до «просто перезапустимо», і саме це інтервʼюер і слухає. Сильна відповідь додає, що ретраї — це засіб виграти час, а не діагноз: кожен систематичний повтор варто хоч раз розкопати до причини.
Як тут перевикористовують сесію логіну і чим це відрізняється від Playwright?
За це відповідає плагін auth: він логінить користувача в першому тесті, зберігає кукі в памʼять або у файл і далі перевикористовує сесію, а якщо вона протухла — логіниться заново сам. У Playwright ту саму задачу закривають у файлі, але за протуханням інструмент не стежить: видалити застарілий файл — робота автора тестів. Тобто CodeceptJS бере на себе те, що там робиться руками. Це не «краще»: автоматичне поновлення сесії зручне рівно доти, доки ви не почали тестувати саму поведінку протухлої сесії — тоді плагін тихо лагодить те, що ви навмисно ламали. Побічна деталь, яка часто плутає: назви autoLogin у чинному реєстрі плагінів немає, потрібну функцію несе auth, тож старий конспект тут заведе не туди.
Сценарного API не вистачає для потрібної дії. Що робите?
У фреймворку є штатний аварійний вихід: окремий плагін відкриває сценарію внутрішній обʼєкт хелпера — той самий page із Playwright, наприклад. Тобто зійти рівнем нижче можна законно, без хаків, і це визнання самого фреймворку, що сценарний стиль — абстракція, а абстракції протікають. Але за цей крок є ціна, і назвати її треба одразу: код, який торкнувся внутрішнього обʼєкта, привʼязаний до конкретного хелпера, і саме тут закінчується «один сценарій для будь-якого бекенда». Тому розумна практика — локалізувати такі місця: тримати їх у кастомних кроках або в обʼєктах сторінки, а не розсипати по сценаріях, щоб при зміні бекенда переписувати одне місце, а не всю сюїту.
Чим I у CodeceptJS відрізняється від актора в патерні Screenplay?
Різна одиниця абстракції. У Screenplay актор — лише один із пʼяти будівельних блоків поряд зі здібностями, інтеракціями, задачами й питаннями; представляти він може і людину, і зовнішню систему, а в одному сценарії акторів буває кілька: один готує дані через API, інший проходить флоу в UI. Одиниця мислення там — задача актора. У CodeceptJS I — насамперед проксі до хелперів, тобто одиниця мислення інша: крок сценарію. Тому пряме порівняння «що краще» некорисне; корисне питання — яку одиницю абстракції ви хочете бачити в тесті. І варто не змішувати два слова «актор» у розмові: якщо інтервʼюер питає про Screenplay, відповідь про I буде відповіддю не на те питання.
Чим CodeceptJS відрізняється від Playwright і коли ви його оберете?
Насамперед це не пара конкурентів на одному рівні: Playwright сам є бекендом і саме він може стояти хелпером під сценарієм. CodeceptJS — надбудова, яка дає спільний читабельний сценарний запис поверх кількох бекендів. Звідси й відповідь про доречність: інструмент виправданий, коли потрібен один сценарний шар над різним виконанням — скажімо, коли веб і мобайл треба тримати в однаковому стилі, або коли сценарії доводиться показувати не тільки автоматизаторам. Синтаксис документація називає BDD-подібним, і тут варто уточнити самому: мова про читабельність запису в JavaScript, а не про Gherkin — сценарій лишається кодом, а не текстом фіч-файла, зіставленим зі . Ціну треба назвати без уточнювального питання: спільна API уніфікує запис, а не поведінку; між тестом і рушієм зʼявляється шар, крізь який ви й будете дебажити, тобто знати треба обидва рівні; аварійний вихід до внутрішнього обʼєкта привʼязує сценарій до бекенда. І назвіть дзеркальну ситуацію, де інструмент нічого не додає: одна команда, один браузерний стек, а тести пишуть інженери, для яких API рушія і так рідна мова — тоді читабельність купується лише зайвим рівнем непрямості, і розумніше взяти той інструмент, який тут і був би хелпером.
Три кейси, у яких сценарний стиль перестає бути «ще одним синтаксисом» і починає впливати на роботу: розбір чужого репозиторію через конфігурацію замість тестів, збирання власного кроку й на , і розслідування трьох різних падінь , які на перший погляд виглядають однаково. Скрізь — на що дивитися і чому.
Кейс 1. Перший день у чужому репозиторії: читаємо конфіг, а не тести
Вам дали доступ до , ви відкриваєте перший тест і бачите десять рядків без жодного селектора, await чи import. Гортати доку інструмента в цей момент — найдовший маршрут: майже все, що виглядає незрозумілим, оголошено в одному файлі конфігурації.
// codecept.conf.ts — карта проєкту, з якої варто починати читання
export const config = {
// 1) ХТО виконує кроки і на якій адресі
helpers: {
Playwright: { url: 'https://app.example.com', browser: 'chromium', show: false },
},
// 2) ЩО прилітає в сценарій за іменем — без жодного import у тесті
include: {
I: './steps_file.ts',
loginPage: './pages/Login.ts',
cartFragment: './fragments/Cart.ts',
},
// 3) ЯКА поведінка ввімкнена поверх сценарію
plugins: {
retryFailedStep: { enabled: true },
auth: { enabled: true },
// сюди ж вмикають артефакти падіння (скріншот, дані зі сторінки, відео)
// і звіт JUnit XML для CI — усі вони приходять разом із фреймворком
},
tests: './tests/**/*_test.ts',
};
Далі сам тест перестає бути загадкою — кожен його рядок має адресу в цьому файлі:
Scenario('товар лишається в кошику після входу', async ({ I, loginPage, cartFragment }) => {
I.amOnPage('/catalog'); // базова адреса — з helpers
I.click('Купити', '.product-card:first-child');
await loginPage.signIn('user@example.com', 'qa-pass'); // обʼєкт — з include
await cartFragment.expectItemsCount(1); // фрагмент — теж ваш код, теж з await
});
Таблиця маршрутів для розслідування:
| Що бачите | Куди дивитися першим | Чому саме туди |
|---|---|---|
| крок падає з «дія не підтримується» | секція helpers + дока цього хелпера | крок виконує бекенд, а не актор |
| обʼєкт у сценарії, якого ніде не імпортовано | секція include | екземпляр створює контейнер за іменем |
крок I.loginAs('admin'), якого немає в доці | файл кастомних кроків з include.I | спільні кроки дописуються до актора |
| крок «іноді доходить із другої спроби» | секція plugins | ретрай кроку ввімкнений рядком у конфізі |
| пароль у логу кроків прихований | код кроку, обгортка secret() | значення навмисно не потрапляє в лог |
Що дивитися і чому:
- Назва хелпера — це половина відповіді на будь-яке «чому крок так поводиться». лише називає дію, а протокол, браузер і межі можливого дає бекенд. Тому «у CodeceptJS немає такої дії» майже завжди перекладається як «її немає в нашому хелпері» — і перевіряється це за одну хвилину, якщо ви знаєте, який рядок конфігу читати.
- Відсутність
importу тесті — норма, а не поламаний файл. Обʼєкти оголошені вincludeй приходять у сценарій за іменем; шукати треба конфіг, а не рядок імпорту. Якщо обʼєкт не резолвиться в сценарії, перша підозра — одрукована назва саме тут, а не проблема зі шляхом до файлу. - Плагіни — це не магія фреймворку, а рядки, які хтось увімкнув. Автоматичний перелогін, при падінні, повтор кроку — усе це видно в конфізі. Читання проєкту з цієї секції одразу пояснює, чому перший тест повільніший за решту (логін) і чому падіння супроводжується артефактами.
- Порядок читання економить години.
helpers→include→plugins→ сам тест. Зворотний порядок (тест → дока → гугл) веде на сторінки Playwright, тоді як причина живе рівнем вище.
Кейс 2. Свій крок і page object: контейнер, await, secret()
Задача звичайна: винести логін в окреме місце, щоб десяток сценаріїв не повторював одні й ті самі поля. У класичному ви б зробили або базовий клас — тут інша механіка доставки.
// pages/Login.ts — сам обʼєкт; він нічого не експортує в тести напряму
const { I } = inject(); // ледачий проксі: резолвиться в момент виклику, не зараз
class LoginPage {
private email = { css: '#email' };
private password = { css: '#password' };
private submit = { role: 'button', name: 'Увійти' };
async signIn(email: string, password: string) {
I.amOnPage('/login');
I.fillField(this.email, email);
I.fillField(this.password, secret(password)); // у лог кроків значення не піде
I.click(this.submit);
I.see('Кабінет', 'nav');
}
}
export default LoginPage; // клас, а не екземпляр — обʼєкт створить контейнер
Спільний крок, який має бути доступний скрізь, дописується не в базовий клас, а до самого актора:
// steps_file.ts — те, що потрапить у include.I
export = function () {
return actor({
loginAsAdmin() {
this.amOnPage('/login');
this.fillField('Email', 'admin@example.com');
this.fillField('Password', secret(process.env.ADMIN_PASSWORD));
this.click('Увійти');
},
});
};
І сценарій, який цим користується:
Scenario('адмін бачить панель керування', async ({ I }) => {
I.loginAsAdmin(); // дія актора — у чергу стає сама
I.see('Керування', 'nav');
const plan = await I.grabTextFrom('.account-plan'); // grab — тільки з await
I.say(`тарифний план акаунта: ${plan}`);
});
Scenario('звичайний користувач панелі не бачить', async ({ I, loginPage }) => {
await loginPage.signIn('user@example.com', 'qa-pass'); // ваш код — теж з await
I.dontSee('Керування', 'nav');
});
Що дивитися і чому:
- Правило
awaitтут читається одним рядком: чекають те, що не є дією актора.I.loginAsAdmin()іI.see()живуть у спільній черзі, аgrabTextFromповертає значення, і метод page object — це вже ваша функція. Забутийawaitпередgrabдає найпідступніший симптом: у змінній лежить , порівнює його з рядком і падає повідомленням, у якому про це немає ні слова. inject()на початку файлу безпечний саме тому, що ледачий. Ви деструктуризуєтеIдо того, як узагалі створив обʼєкти, і це працює: підставить справжнього актора в момент виклику методу. Тому не варто «на всяк випадок» переноситиinject()усередину кожного методу — код від цього тільки шумніший.- Експортується клас, а не
new LoginPage(). Екземпляр створює контейнер, коли резолвить імʼя зіinclude; це та сама причина, через яку тест нічого не імпортує. Якщо в репозиторії ви бачите обидва стилі одночасно — це слід міграції, і читати конфіг доводиться уважніше. - Пароль заходить у крок через
secret(), а не через рядок. Обгортка ховає значення в логу кроків — це важливо не лише для безпеки: артефакти падіння з CI розлітаються по каналах, тікетах і скріншотах, і саме там найчастіше «випадково» опиняється робочий пароль тестового користувача. - — той самий механізм, інша назва в голові.
cartFragmentіз першого кейсу технічно нічим не відрізняється відloginPage: різниця концептуальна — фрагмент описує автономну частину інтерфейсу (модалку, віджет, кошик), а не сторінку цілком. Обидва оголошуються в тій самій секції.
Кейс 3. Три падіння локатора, які виглядають однаково
Найчастіший діагноз новачка на цьому інструменті — «локатори тут нестабільні». Насправді за однаковим симптомом стоять три різні механізми, і лікуються вони по-різному.
// те, що падає
I.click('Зберегти'); // симптом 1: тисне не ту кнопку
I.fillField('input', 'test@qa'); // симптом 2: ElementNotFound
I.click({ css: '.row button' }); // симптом 3: тисне першу з десяти, мовчки
| Симптом | Що відбулося насправді | Лікування |
|---|---|---|
| дія пішла не на той елемент | підпис Зберегти є і в модалці, і у формі під нею | другий аргумент — контекст пошуку |
ElementNotFound на слові, яке точно є на екрані | рядок пройшов увесь ланцюг і був спробуваний як CSS-селектор | назвати стратегію явно або дати справжній підпис поля |
| дія пішла на перший із багатьох збігів | явна стратегія гарантує один запит, а не один результат | звузити селектор або обмежити зону контекстом |
Виправлений варіант:
I.click('Зберегти', '.modal'); // шукаємо тільки всередині модалки
I.fillField('Email', 'test@qa.example'); // підпис поля, а не назва тега
I.click({ css: '.row.is-active button' });
Що дивитися і чому:
- Контекст — це рекомендований дефолт, а не аварійний захід. Другий аргумент одночасно прибирає збіги з дублікатами підписів і звужує простір пошуку, тобто гасить ту саму ціну кількох запитів, через яку семантичні локатори лають. Втеча на CSS-селектор при першому ж виглядає як «зробили надійніше», а насправді просто міняє читабельний рядок на крихку привʼязку до розмітки.
- Слово
inputу лапках — не назва поля, а майже завжди селектор. Останній крок ланцюга трактує нерозпізнаний рядок як CSS, тому помилка приходить не там, де ви на неї чекали. Правило для себе: у семантичному локаторі має стояти те, що бачить користувач (підпис, плейсхолдер, текст кнопки), а не те, що бачить фронтендер у DevTools. - Явна стратегія не робить локатор однозначним. «Строгість» тут означає, що хелперу не треба перебирати варіанти, — про кількість знайдених елементів вона не каже нічого. Хто переносить сюди звичку зі строгого режиму Playwright, отримує найтихіший клас багів: тест зелений, але клацає не по тому рядку таблиці.
- Ролевий локатор ламається разом із семантикою розмітки.
{ role: 'button', name: 'Увійти' }резолвиться через дерево доступності (accessibility tree), тож переїзд фронтенду зbuttonна клікабельнийdivламає тест, хоча візуально не змінилося нічого. Це не привід тікати від ролей — це привід принести падіння фронтендерам як баг доступності. - Увімкнений кроку робить діагностику важчою. Частина таких падінь не почервоніє, а сховається за другою спробою: тест зелений, прогін повільніший. Тому при полюванні на нестабільні локатори дивляться не лише на статус, а й на тривалість кроків — раптове зростання часу зазвичай і є той повтор, якого ви не бачили.
Актор і черга команд
- Можу пояснити, що
I— це (actor): абстракція користувача-тестувальника й до ввімкнених хелперів, тожI.click()не виконує клік, а лише ставить крок у чергу для бекенда — і саме туди дивлюся першим при розслідуванні падінь. - Знаю, чому тест виглядає синхронним: дії актора зчіплюються в один глобальний ланцюг і стають у чергу автоматично, а очікування вбудоване в самі дії, тож явні очікування потрібні рідко.
- Памʼятаю межу цієї автоматики:
awaitобовʼязковий для командgrab, для імпортованих функцій і для методів обʼєктів сторінки — черга не покриває мій власний код. - Знаю обгортку
secret()і чому пароль у кроці варто заводити саме через неї: значення не потрапляє в лог кроків.
Хелпери і конфігурація
- Можу пояснити, що хелпер (helper) — це бекенд, який реально виконує кроки, а актор лише називає дію; знаю бекенди й протоколи за ними: Playwright (власний протокол), WebDriver (
wire-протокол W3C), Puppeteer (протокол DevTools), Appium (мобільні застосунки). - Знаю, що хелпер і базовий URL задаються в конфігурації (секція
helpers), і що читання чужого репозиторію починається саме звідти. - Розрізняю «легко мігрувати» і «працює однаково»: спільна API уніфікує запис, гарантованої сумісності бекендів немає — контрприклад із доки про заголовки запиту (є в Playwright і Puppeteer, немає у WebDriver).
Локатори
- Знаю два штатних види : обʼєкт із явно названою стратегією (
{ css: ... },{ xpath: ... },{ id: ... },{ role: ..., name: ... }), який резолвиться одним запитом, і семантичний рядок, що його інструмент звіряє з підписами полів, написами кнопок, плейсхолдерами таaria-*— ціною кількох дешевих запитів; памʼятаю й те, що CSS найшвидший — у гарячому циклі з тисячами звернень це вирішує. - Знаю, що семантичний локатор із контекстом (другим аргументом) — рекомендований дефолт, і що саме контекст прибирає збіги з дублікатами підписів.
- Памʼятаю кінець ланцюга розвʼязання (нерозпізнаний рядок трактується як CSS-селектор — звідси «дивні»
ElementNotFound), те, що ролевий локатор резолвиться через дерево доступності (accessibility tree), і те, що «строгий локатор» тут (однозначна стратегія пошуку) — не строгий режим Playwright (єдиний збіг серед знайдених елементів).
Page object і кастомні кроки
- Можу сформулювати сам патерн: обʼєкт обгортає сторінку чи фрагмент застосунковим API, і в одне місце знає структуру розмітки цієї частини; змінюється не патерн, а механіка доставки.
- Знаю, що тут не імпортують: імʼя оголошується в секції
include, у сценарій він приходить параметром або черезinject(), механізм — (dependency injection), де екземпляр створює , аinject()віддає ледачий проксі, тож деструктуризуватиIдо означення класу безпечно. - Памʼятаю, що спільні кроки розширюють актора через файл кастомних кроків, а не базовий клас тесту чи , і що технічно тотожні обʼєктам сторінки (різниця лише концептуальна).
- Не змішую
Iз актором у Screenplay: там актор — один із пʼяти блоків, представляє й зовнішні системи, і акторів у сценарії може бути кілька.
Плагіни
- Розумію, що плагіни в поставці (помітна частина «магії фреймворку» — це рядок у секції
plugins), і щоretryFailedStepповторює кожен невдалий крок, тобто працює на рівні кроку, а не тесту. - Знаю, що логін раз на прогін дає плагін
auth(кукі в памʼять або файл, автоматичний перелогін при протуханні), назвиautoLoginу чинному реєстрі немає, і можу порівняти це з Playwright, де за протуханням збереженого стану інструмент не стежить. - Знаю, що артефакти падіння (, збір даних зі сторінки після невдалого тесту — рекомендовано для CI, відео у WebM через screencast API Playwright) і звіт JUnit XML — це окремі плагіни.
- Розумію, що плагін між діями маскує повільність застосунку, а штатний доступ до внутрішнього обʼєкта хелпера (наприклад,
pageіз Playwright) привʼязує сценарій до конкретного бекенда.
Коли інструмент доречний
- Можу назвати сценарій, де надбудова виправдана: один читабельний сценарний шар над різними бекендами — веб і мобайл в одному стилі, або команда, де сценарії читають не лише автоматизатори; і розумію, що «BDD-подібний синтаксис» тут про читабельність запису в JavaScript, а не про Gherkin зі .
- Можу назвати ціну сам: додатковий рівень непрямості й дебаг крізь два рівні, відсутність гарантованої взаємозамінності бекендів — і дзеркальну ситуацію, де інструмент нічого не додає: одна команда, один браузерний стек і інженери, що працюють з API рушія без .
Квіз
Перед стартом
- Питань: 16
- Поріг «зараховано»: ≥70% правильних відповідей.
- Результат впливає на прогрес; завалені питання підуть у чергу повторення.
- Квіз впливає на компліт теми: тема стає «пройдено», лише коли прочитано теорію І квіз складено на ≥70%.
Питання
Актор `I` у CodeceptJS — що це?