QA Interview Hub · База знань · Тести
Cypress
Локатори й робота з DOM
пошук елементів і взаємодія
Чим cy.get() відрізняється від .find() і cy.contains()?
cy.get(selector) шукає від кореня документа, навіть якщо стоїть у ланцюжку. Виняток — усередині .within().
.find(selector) шукає лише всередині попереднього subject.
cy.contains(text) шукає елемент за текстом; можна вказати селектор: cy.contains('button', 'Зберегти'). У ланцюжку шукає всередині subject.
cy.get('[data-cy=order]').find('[data-cy=price]'); // ціна саме в замовленні
cy.get('[data-cy=order]').get('[data-cy=price]'); // ⚠️ шукає по всій сторінці
cy.contains('button', 'Оформити').click();
На співбесіді: cy.get завжди шукає від кореня документа, навіть якщо стоїть у ланцюжку. Find шукає тільки всередині попереднього елемента. А contains знаходить елемент за текстом, і йому можна передати селектор, щоб звузити пошук, наприклад кнопку з певним текстом.
А в Playwright:page.locator() шукає по сторінці, locator.locator() — всередині іншого локатора, текст — getByText(). Важлива відмінність — strict mode: якщо локатор для дії знайшов кілька елементів, Playwright одразу падає з помилкою.
Які селектори рекомендує Cypress і чому?
Найстабільніші — окремі атрибути для тестів: data-cy, data-test, data-testid. Вони не змінюються при рефакторингу стилів чи верстки.
Класи та id для стилів крихкі, позиційні селектори на кшталт div:nth-child(3) — найгірші. cy.contains доречний, коли текст є частиною вимоги. Для семантичних локаторів у стилі Playwright є плагін Testing Library з cy.findByRole.
cy.get('[data-cy=submit]'); // ✅
cy.contains('button', 'Увійти'); // ✅ якщо текст — вимога
cy.get('.btn.btn-primary.mt-2'); // ⚠️ залежить від стилів
cy.get('form > div:nth-child(3) > button'); // ❌ ламається від верстки
На співбесіді: Найкраще — окремі атрибути для тестів, наприклад data-cy: їх не чіпають при зміні стилів. Contains за текстом — коли текст сам є вимогою. Класи для стилів і позиційні селектори я не використовую, бо вони ламаються від будь-якої зміни верстки. Якщо потрібні семантичні локатори, є плагін Testing Library з findByRole.
А в Playwright:Семантичні локатори вбудовані: getByRole, getByLabel, getByPlaceholder, getByTestId. Атрибут для getByTestId налаштовується в конфігу через testIdAttribute, наприклад на data-cy.
Як звузити пошук: within, eq, first, filter?
.within() обмежує всі вкладені команди одним блоком — зручно для рядка таблиці чи картки товару. .eq(n), .first(), .last() беруть елемент за індексом. .filter() залишає елементи, що відповідають селектору або тексту через :contains().
Індекси використовуй лише тоді, коли порядок є частиною логіки. Інакше прив'язуйся до вмісту.
cy.contains('[data-cy=row]', 'Замовлення #1042').within(() => {
cy.get('[data-cy=status]').should('have.text', 'Відправлено');
cy.get('[data-cy=cancel]').click();
});
cy.get('[data-cy=product]').filter(':contains("Термос")').first().click();
На співбесіді: Within обмежує пошук одним блоком: знаходжу потрібний рядок таблиці за текстом, і всі команди всередині шукають лише в ньому. Eq, first і last беруть за індексом, але я користуюся ними, тільки коли порядок справді важливий. Інакше звужую за вмістом через contains або filter.
А в Playwright:Замість within — композиція локаторів, замість filter(':contains') — filter({ hasText }).
const row = page.getByTestId('row').filter({ hasText: 'Замовлення #1042' });
await expect(row.getByTestId('status')).toHaveText('Відправлено');
await row.getByTestId('cancel').click();
Чому { force: true } небезпечне?
Опція force: true вимикає перевірки actionability: Cypress більше не перевіряє, чи елемент видимий, чи не перекритий модалкою, чи не disabled. Подія просто стріляє по елементу.
Якщо force «полагодив» тест, тест тепер проходить попри реальну проблему: користувач не зміг би клікнути, а тест зелений. Виправдане використання рідкісне — наприклад, прихований нативний input під кастомним чекбоксом — і поруч має бути коментар чому.
// ⚠️ лише з поясненням
// нативний input прихований, кастомний чекбокс перекриває його
cy.get('input[type=checkbox]').check({ force: true });
На співбесіді: Force вимикає перевірки: чи елемент видимий, чи не перекритий, чи активний. Якщо тест почав проходити тільки з force, найімовірніше він тепер приховує справжній баг — користувач клікнути не зміг би. Я використовую його лише в рідкісних випадках, наприклад для прихованого нативного чекбокса, і завжди з коментарем.
А в Playwright:У Playwright є такий самий { force: true }, і ризики ті самі: він вимикає перевірки actionability.
Як працювати з iframe, новими вкладками, завантаженням і вивантаженням файлів?
- Iframe. Окремої команди немає. Дістають
contentDocument.body і загортають через cy.wrap, або використовують плагін cypress-iframe. Iframe з іншого домену недоступний через політику same-origin.
- Нові вкладки. Cypress працює лише в одній. Прибирають
target="_blank" і відкривають посилання в тій самій вкладці, або просто перевіряють атрибут href.
- Upload. Вбудована команда
.selectFile(), з підтримкою drag-and-drop.
- Download. Перевіряють файл у папці
cypress/downloads через cy.readFile.
cy.get('iframe#payment')
.its('0.contentDocument.body').should('not.be.empty')
.then(cy.wrap)
.find('[data-cy=card]').type('4242424242424242');
cy.get('[data-cy=docs-link]').invoke('removeAttr', 'target').click();
cy.get('input[type=file]').selectFile('cypress/fixtures/avatar.png');
cy.readFile('cypress/downloads/report.csv').should('contain', 'Total');
На співбесіді: Для iframe я дістаю contentDocument body і загортаю його в cy.wrap, або беру плагін cypress-iframe, але iframe з іншого домену недоступний. Нові вкладки Cypress не підтримує, тому я прибираю target blank або перевіряю href. Для завантаження файлів є вбудований selectFile, а скачаний файл перевіряю через readFile у папці downloads.
А в Playwright:Тут Playwright помітно сильніший. Iframe — page.frameLocator(), зокрема з іншого домену. Нові вкладки — context.waitForEvent('page'). Upload — setInputFiles(), download — page.waitForEvent('download').
await page.frameLocator('iframe#payment').getByTestId('card').fill('4242...');
const [tab] = await Promise.all([
page.context().waitForEvent('page'),
page.getByTestId('docs-link').click(),
]);
Селектори і data-cy
Найкраща практика Cypress — окремі тестові атрибути data-cy, data-test або data-testid. Вони не змінюються разом зі стилями чи текстом і явно показують розробникам, що елемент використовується в тестах. Селектор виглядає так: cy.get('[data-cy=submit]').
Слід уникати класів CSS, особливо згенерованих, тегів і позиційних селекторів на кшталт div > span:nth-child(3). Вони ламаються при будь-якій зміні верстки. ID підходить, якщо він стабільний і не генерується динамічно.
cy.contains добре працює, коли текст — частина того, що тестується, але чутливий до змін копірайту й локалізації. Для доступності та стабільності часто додають Testing Library для Cypress з пошуком за роллю і назвою.