vyvchy
    Теми розділу

    07 · Інструменти автоматизації

    Playwright: локатори і дії з елементами

    Зміст

    Тест падає з «element not found», і півгодини йде на пошук бага, якого немає: кнопка на місці, просто (locator) описував її ланцюжком CSS-класів, а фронтендер перейменував один клас. Це класична поломка в UI-автоматизації — і саме тому Playwright має щодо локаторів позицію: у нього є перелік рекомендованих способів пошуку, він відмовляється працювати з неоднозначним збігом і сам перевіряє, чи елемент готовий до дії. Джун, який знає тільки page.locator('.btn-primary'), зазвичай пише набір, який доводиться ремонтувати після кожної помітної зміни верстки.

    Вузлів тут три: пріоритет локаторів, strict mode і force: true. Ця глава — про механіку: чим локатор відрізняється від знайденого елемента, як звужувати пошук і що інструмент робить між викликом click() і справжнім кліком. CSS-селектори, XPath і DOM як явище — у главі «DOM, селектори та події»; ієрархія браузер → контекст → сторінка — у главі «Playwright: архітектура, браузери і контексти»; ретрайні перевірки й — у наступній главі «Playwright: перевірки (assertions) і автоочікування».

    Локатор — це рецепт пошуку, а не знайдений елемент

    Офіційна дока формулює це прямо: локатори — «спосіб знайти елемент(и) на сторінці в будь-який момент», і саме на них тримаються (auto-waiting) та ретрайність інструмента. Різниця з інтуїцією принципова: локатор не зберігає посилання на вузол DOM — він зберігає опис, за яким вузол шукається заново в момент дії, а не в момент оголошення.

    Три наслідки:

    • локатор можна створити один раз — у змінній, — і перевикористовувати скільки треба: тримати він нічого не тримає;
    • сторінка може перемалюватися між оголошенням локатора і дією, і це нормальний сценарій, а не гонка: пошук усе одно відбудеться пізніше;
    • два звертання до одного локатора — це два незалежні пошуки, тож на сторінці, що довантажується, результати можуть відрізнятися.
    import { test } from '@playwright/test';
    
    test('оплата картою', async ({ page }) => {
      await page.goto('/checkout');
    
      // локатор оголошено один раз; пошук відбудеться в момент click()
      const pay = page.getByRole('button', { name: 'Оплатити' });
    
      await page.getByLabel('Номер картки').fill('4111111111111111');
      await pay.click();
    });

    Сім вбудованих локаторів і чому роль перша

    Дока називає рекомендованими сім вбудованих локаторів:

    ЛокаторЩо шукає
    getByRole()за явними й неявними атрибутами доступності
    getByText()за текстовим вмістом
    getByLabel()елемент керування формою — за текстом повʼязаного підпису
    getByPlaceholder()поле введення — за його placeholder
    getByAltText()елемент (зазвичай зображення) — за текстовою альтернативою
    getByTitle()за атрибутом title
    getByTestId()за атрибутом data-testid (сам атрибут налаштовний)

    Перелік не є жорстким рейтингом, але окрема рекомендація в доці є: щоб тести були стійкими, пріоритезувати треба user-facing атрибути й явні контракти — «наприклад, page.getByRole()». Причина не в естетиці. Роль відображає, як сторінку сприймають користувач і допоміжні технології: кнопка це чи чекбокс. А отже, локатор за роллю не залежить від верстки — рефакторинг і перейменування класів його не ламають, а .btn-primary.mt-3 > span ламають.

    Механізм під цим — дерево доступності (accessibility tree): браузер будує його на основі DOM окремим, паралельним поданням сторінки, і саме звідти допоміжні технології беруть пару «роль + доступне імʼя» (для кнопки «OK» читач екрана може вимовити «push-button OK»). Тому разом із роллю зазвичай слід передавати й доступне імʼя — інакше локатор не прицілюється в конкретний елемент.

    Звідси побічний ефект, який варто називати на співбесіді: якщо «кнопка» насправді клікабельний <div> без ролі, пошук за роллю її не знайде — і це не незручність інструмента, а знайдений дефект. Елемент без доступного імені або з неправильною роллю — одночасно дефект доступності й майбутній нестабільний локатор.

    Поділ, який дока подає як рекомендацію, а не заборону: інтерактивні елементи — за роллю; неінтерактивні (div, span, p) — за текстом.

    // інтерактивне — за роллю з доступним імʼям
    await page.getByRole('button', { name: 'Надіслати' }).click();
    await page.getByRole('link', { name: 'Умови використання' }).click();
    
    // поле форми — за підписом
    await page.getByLabel('Електронна пошта').fill('qa@example.com');
    
    // неінтерактивний текст — за текстом
    await page.getByText('Замовлень не знайдено');

    Філософія під цим не Playwright-специфічна — її сформулювала Testing Library: що більше твої тести схожі на те, як користуються твоїм ПЗ, то більше довіри вони дають.

    Так

    Ні

    Так

    Ні

    Так

    Ні

    Так

    Ні

    Потрібен елемент

    Інтерактивний?

    getByRole + доступне імʼя

    Поле форми з підписом?

    getByLabel

    getByText

    Збіг єдиний?

    Готово

    Звузити: chaining + filter

    Однозначність досягнута?

    getByTestId: явний контракт

    CSS або XPath: свідомо, з ціною

    Так

    Ні

    Так

    Ні

    Так

    Ні

    Так

    Ні

    Потрібен елемент

    Інтерактивний?

    getByRole + доступне імʼя

    Поле форми з підписом?

    getByLabel

    getByText

    Збіг єдиний?

    Готово

    Звузити: chaining + filter

    Однозначність досягнута?

    getByTestId: явний контракт

    CSS або XPath: свідомо, з ціною

    getByTestId: найстійкіший, але не user-facing

    (test id) — це явний контракт між тестом і кодом. Дока не називає його «найкращим»: вона називає його найстійкішим — навіть якщо зміниться текст або роль, тест лишиться зеленим — і одразу застерігає, що він не user-facing. Якщо роль чи текст для перевірки важливі, брати треба user-facing локатори.

    Що ще каже джерело поіменно:

    • визначати явні тест-ідентифікатори мають QA й розробники разом — це домовленість, а не приватна ініціатива автотестера;
    • за замовчуванням getByTestId() шукає за атрибутом data-testid, і атрибут можна переналаштувати в конфігу;
    • доречний він тоді, коли ти обрав цю методологію — або коли локатор за роллю чи текстом побудувати не вдається.

    І data-* тут не «засмічення розмітки»: це штатний механізм HTML — атрибут, чиє імʼя починається з data-, тримає на семантичному елементі додаткову інформацію без хаків, і сам HTML спроєктований із розрахунком на такі дані: привʼязані до елемента, але без визначеного значення для самої розмітки.

    Далі — дельта, про яку легко забути: «пріоритет локаторів» не є універсальним законом. Cypress ставить data-* основною стратегією й дає явну шкалу:

    Селектор у CypressВердикт доки
    cy.get('button'), cy.get('.btn.btn-large')Never
    cy.get('#main'), cy.get('[name="submission"]')Sparingly
    cy.contains('Submit')Depends — «все ще зчеплено з текстом, який може змінитися»
    cy.get('[data-cy="submit"]')Always — «ізольовано від усіх змін»

    Тобто текст тут нижче тест-атрибута, тоді як Playwright і Testing Library ставлять user-facing атрибути вище. Це не «хтось помиляється», а різні акценти: стійкість проти близькості до користувача.

    Коли CSS і XPath ще виправдані

    Дока не забороняє їх, але й не помʼякшує формулювання: «якщо тобі абсолютно необхідно» використати CSS або XPath — є page.locator(), який приймає селектор. Причина обережності названа: вони не рекомендовані, бо DOM часто змінюється й тест перестає бути стійким. CSS добирає елементи за типом, класом, ідентифікатором, атрибутом і станом — і навіть за позицією в дереві, а позиційний добір (:nth-child) крихкий за побудовою: додали рядок вище — і селектор указує вже на інший елемент. Але два випадки, де CSS і XPath лишаються робочим інструментом, є.

    Перший — часткове зіставлення за атрибутом. Коли генерує класи з випадковим хешем (Button_root__a1b2c), рятує саме воно: ^= — «починається з», $= — «закінчується на», *= — «містить входження підрядка». Тут же живе пастка: ~= до цієї трійки не належить — він добирає елементи, чиє значення атрибута є списком слів через пробіл, серед яких є рівно таке слово. Тому [class~="logo"] знайде class="header logo", але не class="logotype".

    Другий — рух угору по дереву й зіставлення за повним текстом. XPath має осі, яких у наборі комбінаторів CSS немає: parent, ancestor, preceding-sibling.. — це рівно parent::node()). Частину цих випадків CSS покриває реляційним псевдокласом :has(), який дає дібрати предка або попереднього сусіда, — але за статусом Baseline він доступний в усіх браузерах лише з грудня 2023 року.

    Платня за XPath — багатослівність і дві класичні пастки:

    • //button[1] — це не «перша кнопка на сторінці», а всі кнопки, які є першими серед кнопок-дітей свого батька: специфікація застерігає, що //para[1] не те саме, що /descendant::para[1]. Справді перший збіг беруть у дужки: (//button)[1].
    • contains(text(), 'Оплата картою') не знайде кнопку, бо text() повертає множину вузлів, а конвертація такої множини в рядок бере вузол, перший у порядку документа: для <button>Оплата <b>картою</b></button> перевіряється лише «Оплата ». Ліки — . замість text(): рядкове значення елемента конкатенує всіх його текстових нащадків.

    І окремо про межу, де XPath перестає працювати: локатори Playwright типово працюють з елементами всередині Shadow DOM, але з двома винятками — XPath межу не пробиває, а закриті (closed) shadow root локатори не підтримують. Тож у застосунку на web components XPath як стратегія відпадає.

    Головне: якщо в елемента немає ні ролі, ні доступного імені, ні стабільного тексту — правильна відповідь не «довший CSS-шлях», а домовленість про тест-ідентифікатор.

    Chaining і filter(): звужувати, а не ускладнювати

    Коли на сторінці двадцять однакових карток, спокуса одна — дописати до селектора ще один рівень вкладеності. Правильний хід інший: локатори можна зчіплювати, звужуючи пошук до конкретної частини сторінки. Спершу контейнер, у якому елемент унікальний, потім елемент уже в ньому.

    Тут є правило, на якому спотикаються всі: фільтрувальний локатор мусить бути відносним до вихідного й шукається від його збігу, а не від кореня документа.

    // картка потрібного замовлення: контейнер, у якому є цей текст
    const card = page
      .getByRole('listitem')
      .filter({ has: page.getByText('Замовлення 1042') });
    
    // кнопка шукається вже всередині картки, а не по всій сторінці
    await card.getByRole('button', { name: 'Скасувати' }).click();

    В іншого інструмента той самий прийом — головний патерн, а не трюк. CodeceptJS розрізняє строгий локатор (обʼєкт із явно названою стратегією — один запит) і семантичний (рядок, який зіставляється так, як сторінку читає користувач), а рекомендованим за замовчуванням називає семантичний локатор у контексті. Мотив названо прямо: дублікати підписів деінде на сторінці більше не дають нестабільних збігів. Ціна теж названа чесно — семантичний пошук робить кілька запитів, і саме контекст робить цю ціну незначущою.

    // CodeceptJS: підпис + контекст, у якому він унікальний
    I.click('Скасувати', '.order-card');

    Строгість локатора: «знайшло два» — це фейл, а не дрібниця

    Локатори Playwright строгі: будь-яка операція, що передбачає цільовий елемент DOM, кидає виняток, якщо збігів більше за один. Джуна це спершу дратує — насправді це одна з найкорисніших властивостей інструмента.

    Подумай, що означає альтернатива. Якщо фреймворк на неоднозначному збігу мовчки бере перший елемент, тест зелений не тому, що продукт працює, а тому, що інструмент угадав. Строгість перетворює «угадав» на явний фейл — і не випадково перша ж перевірка перед кліком це те, що локатор вказує рівно на один елемент.

    Вийти зі строгості можна явно: first(), last(), nth() кажуть, який зі збігів брати. Але дока супроводжує це застереженням, і воно не про стиль — сторінка змінюється, і локатор укаже на елемент, якого ти не мав на думці. Порада джерела: не глушити строгість, а придумати локатор, який проходить критерій однозначності.

    Практичний бік: strict mode violation у логу — часто не проблема тесту, а знахідка: два однакові доступні імена на одній сторінці варто перевірити як можливий дефект.

    Списки: коли елементів справді багато

    Локатор, що дає двадцять збігів, — це нормально. Ненормально — виконувати над ним дію. Тому робочих шляхів рівно два: звузити до одного елемента зчепленням і фільтром за контекстом (той спосіб, який дока радить) або явно вийти зі строгості через nth(), first(), last(), свідомо взявши , що на зміненій сторінці індекс поїде.

    Питання «а скільки їх?» — це вже не питання дії, а питання перевірки, і тут різниця між гетером і ретрайним критична. Ретрайна перевірка повторює умову, доки вона не виконається, а гетер isVisible() не чекає жодної секунди: дивиться, чи локатор там, і повертає результат одразу. Кількість, знята гетером у випадковий момент, — це гонка з рендерингом. Механіку ретрайних перевірок і таймаутів розбирає наступна глава «Playwright: перевірки (assertions) і автоочікування».

    Actionability: що відбувається між click() і кліком

    Найважливіше в цій главі — те, чого не видно в коді. Перед кожною дією Playwright виконує набір перевірок придатності до дії (actionability), чекає, поки всі релевантні пройдуть, і лише тоді діє. Не дочекався в межах таймауту — падає з TimeoutError.

    Для кліку перевіряється пʼять умов, і кожна має точне означення:

    ПеревіркаЩо саме означає
    Однозначністьлокатор вказує рівно на один елемент
    Visibleнепорожній bounding box і без обчисленого visibility:hidden
    Stableтой самий bounding box щонайменше два послідовні кадри анімації
    Receives Eventsелемент є ціллю вказівникової події в точці дії
    Enabledне задизейблений

    Деталі, які й дають більшість «незрозумілих» падінь:

    • елементи нульового розміру й display:none невидимі, а opacity:0 вважається видимим;
    • стабільність означає, що елемент уже не рухається: поки триває анімація, дія перечікується;
    • «отримує події» ловить рівно ту ситуацію, коли клік перехоплює накладений зверху елемент — оверлей, модалка, тост;
    • «увімкнений» — не лише відсутність атрибута disabled: задизейбленим робить і батьківський fieldset, і aria-disabled="true". Для дій уведення до переліку додається перевірка редагований: увімкнений і не readonly.

    Ні

    Так

    Ні

    Так

    Ні

    Так

    Ні

    Так

    Ні

    Так

    Ні

    Так

    await locator.click

    Перепитати DOM зараз

    Рівно один збіг?

    Виняток строгості

    Visible

    Чекати далі

    Stable

    Receives Events

    Enabled

    Клік

    Таймаут вичерпано?

    TimeoutError

    Ні

    Так

    Ні

    Так

    Ні

    Так

    Ні

    Так

    Ні

    Так

    Ні

    Так

    await locator.click

    Перепитати DOM зараз

    Рівно один збіг?

    Виняток строгості

    Visible

    Чекати далі

    Stable

    Receives Events

    Enabled

    Клік

    Таймаут вичерпано?

    TimeoutError

    Зі схеми видно головне для читання фейлів: TimeoutError на кліку не означає «елемент не знайдено» — він однаково добре означає «елемент є, але його перекриває оверлей» або «елемент є, але досі їде».

    force: true — не фікс, а глушник

    force вимикає необовʼязкові перевірки придатності. Конкретно: передавши правдиве force у клік, ти перестаєш контролювати, чи справді елемент отримує клік — інструмент клікне по координатах, навіть якщо в цій точці подію забирає оверлей.

    Тепер прочитай це очима тестувальника. Тест був червоний, бо клік перехоплював накладений елемент. Після force: true він зелений — але користувач, який зробить те саме руками, у кнопку не влучить. Це не виправлення, а глушник: дефект лишився в продукті, а сигнал про нього вимкнули власноруч.

    Тому падіння «елемент перехоплює вказівникові події» варто вважати корисним: воно фіксує або реальну проблему UX, або власну помилку тесту — не закритий cookie-банер, невидимий бекдроп модалки, тост, що не встиг зникнути. Лікується це (закрити оверлей, дочекатися зникнення тоста), а не force. Сюди ж пастка з opacity: 0: прозорий елемент за означенням видимий, тож клік по практично невидимій кнопці пройде і без force. Інструментальні засоби боротьби з нестабільністю — у главі «Боротьба з флаком засобами інструмента»; межа між легітимним і маскуванням — тема розділу про стратегію автоматизації.

    Типові помилки

    • «Локатор не знаходить елемент — треба точніший селектор». Насправді перша підозра інша: інший контекст. iframe — це окремий документ, а XPath не пробиває shadow root. Симптом «руками в DevTools бачу, а тест ні» перевіряють на iframe і Shadow DOM, а вже потім правлять селектор.
    • «Додали force: true — і тест став стабільним». Насправді вимкнено перевірку «отримує події»: клік тепер проходить там, де реальний користувач у кнопку не влучив би. Стабільним став не продукт, а звіт.
    • «strict mode violation — це причіпка інструмента». Насправді це вимога однозначності: два збіги означають, що тест не знає, що клікає, а без строгості він зеленів би на першому збігу, яким би той не був.
    • «opacity: 0 — елемент невидимий, дія впаде». Насправді за означенням придатності прозорий елемент видимий, і клік по ньому пройде.
    • «data-testid — найкращий локатор, беремо тільки його». Насправді дока називає його найстійкішим, але не user-facing; там, де важлива роль або текст, user-facing локатор ще й ловить дефекти доступності.
    • «XPath я написав правильно». Дві класики: //button[1] — це не перша кнопка на сторінці, а всі кнопки, які є першими серед кнопок-дітей свого батька (перший збіг — (//button)[1]); а contains(text(), 'Оплата картою') не знайде <button>Оплата <b>картою</b></button>, бо конвертація множини вузлів у рядок бере лише перший текстовий вузол («Оплата ») — треба contains(., ...).

    Підсумок

    • Локатор — опис, за яким елемент шукається заново в момент дії, а не збережене посилання на вузол; саме тому на ньому тримаються автоочікування й ретраї.
    • Пріоритет за докою Playwright: user-facing атрибути й явні контракти. Інтерактивне — за роллю з доступним імʼям, неінтерактивне — за текстом; data-testid — найстійкіший, але не user-facing варіант; CSS і XPath — not recommended.
    • Строгість — фіча, а не примха: неоднозначний збіг падає замість того, щоб угадувати. Штатний спосіб її пройти — звузити пошук зчепленням і фільтром, відносним до вихідного локатора.
    • Перед кліком інструмент перевіряє: рівно один збіг, видимий, стабільний, отримує події, увімкнений. Тому TimeoutError на кліку — не завжди «не знайшов».
    • force: true вимикає необовʼязкові перевірки — зокрема «отримує події»: зелений тест після нього означає, що дефект перекритого кліку поїхав у прод разом із вимкненим сигналом.

    Можливі питання

    • «Який пріоритет локаторів у Playwright і чому?» Перевіряють, чи розумієш ти причину, а не порядок напамʼять. Сильна відповідь: user-facing атрибути й явні контракти, бо роль відображає те, як сторінку сприймають користувач і допоміжні технології, і не залежить від верстки; CSS і XPath дока не рекомендує, бо DOM часто змінюється. Плюс: у Cypress ієрархія інша.
    • «Що таке strict mode і що робити, коли локатор знайшов два елементи?» Дивляться, чи не почнеш ти одразу з first(). Правильно: строгість — вимога однозначності, тож спершу звужуємо пошук зчепленням і фільтром.
    • «Що Playwright робить перед кліком?» Питання на actionability: рівно один збіг, видимий, стабільний, отримує події, увімкнений; для введення додається «редагований». Хто називає точні означення — непорожній , два послідовні кадри анімації, opacity:0 вважається видимим — той показує, що читав доку.
    • «Коли доречний force: true Питання-фільтр. Очікувана відповідь: майже ніколи, бо він перестає контролювати, чи елемент справді отримує клік, — тобто робить зеленим тест поверх реального дефекту.
    • «Чим data-testid кращий і чим гірший за локатор за роллю?» Кращий стійкістю: змінився текст або роль — тест лишається зеленим. Гірший тим, що не user-facing: він не перевіряє, чи елемент доступний користувачеві.
    • «Чому expect(await locator.isVisible()).toBe(true) — погана перевірка?» Гетер повертає моментальний зріз і не чекає жодної секунди, а ретрайна перевірка повторює умову, доки вона не виконається.

    Джерела

    Локатор — це рецепт пошуку, а не знайдений елемент

    • Playwright — Locators — локатор як спосіб знайти елемент у будь-який момент і його роль в автоочікуванні та ретраях.
    • Playwright — Best Practices — локатори приходять з автоочікуванням і ретрайністю: перед кліком інструмент перевіряє придатність елемента до дії.

    Сім вбудованих локаторів і чому роль перша

    • Playwright — Locators — перелік семи рекомендованих локаторів, пріоритет user-facing атрибутів і поділ «інтерактивні за роллю, неінтерактивні за текстом».
    • Playwright — Best Practices — чому опора на структуру DOM крихка: змінився CSS-клас — зламався тест.
    • MDN — Accessibility tree (glossary) — дерево доступності як окреме подання сторінки, побудоване на основі DOM.
    • W3C — Accessible Rich Internet Applications (WAI-ARIA) 1.2 — роль і доступне імʼя як пара, яку зчитує допоміжна технологія.
    • Testing Library — Guiding Principles — принцип «що більше тести схожі на реальне використання, то більше довіри».

    getByTestId: найстійкіший, але не user-facing

    • Playwright — Locators — test id як найстійкіший, але не user-facing спосіб; налаштовний атрибут; спільна домовленість QA й розробки.
    • MDN — Use data attributesdata-* як штатний механізм HTML для додаткових даних на елементі.
    • Cypress — Best Practices — інша ієрархія в іншого інструмента: шкала Never/Sparingly/Depends/Always і data-* як основна стратегія.

    Коли CSS і XPath ще виправдані

    • Playwright — Locatorspage.locator() для CSS і XPath, формулювання not recommended з названою причиною, і межа: XPath не пробиває shadow root.
    • MDN — CSS selectors — за чим CSS добирає елементи й чому позиційний добір крихкий.
    • MDN — Attribute selectors — часткове зіставлення ^=, $=, *= і відмінність ~= як добору за словом зі списку.
    • MDN — :has() CSS pseudo-class — добір предка або попереднього сусіда й статус Baseline із грудня 2023 року.
    • W3C — XML Path Language (XPath) Version 1.0 — осі вгору по дереву, пастка //x[1] і конвертація множини вузлів у рядок за першим вузлом.
    • MDN — Using shadow DOM як межа інкапсуляції, крізь яку звичайний пошук не бачить.

    Chaining і filter(): звужувати, а не ускладнювати

    • Playwright — Best Practices — локатори можна зчіплювати, щоб звузити пошук до частини сторінки.
    • Playwright — Locators — фільтрувальний локатор мусить бути відносним до вихідного й шукається від його збігу, а не від кореня документа.
    • CodeceptJS — Locators — семантичний локатор у контексті як рекомендований патерн, його мотив і ціна.

    Строгість локатора: «знайшло два» — це фейл, а не дрібниця

    Списки: коли елементів справді багато

    • Playwright — Locators — локатор як спосіб знайти елементи в будь-який момент і явний вихід зі строгості для наборів.
    • Playwright — Best Practices — ретрайна перевірка чекає, ручний гетер не чекає жодної секунди.
    • Playwright — Auto-waiting (actionability) — ретрайні перевірки повторюють умову, доки вона не виконається.

    Actionability: що відбувається між click() і кліком

    • Playwright — Auto-waiting (actionability) — набір перевірок перед дією, TimeoutError при недочіканні й точні означення видимості, стабільності, отримання подій, увімкненості та редагованості.
    • Playwright — Locators — однозначність збігу як умова операції над локатором.
    • Playwright — Best Practices — автоочікування як перевірка придатності, а не пауза.

    force: true — не фікс, а глушник

    • Playwright — Auto-waiting (actionability)force вимикає необовʼязкові перевірки й перестає контролювати отримання кліку; падіння «перехоплює події» як корисний сигнал; opacity:0 вважається видимим.

    Типові помилки

    Підсумок

    • Playwright — Locators — локатор як спосіб знайти елемент у будь-який момент, пріоритет user-facing атрибутів, статус test id, строгість і вимога відносності фільтрувального локатора.
    • Playwright — Auto-waiting (actionability) — перелік перевірок перед кліком, TimeoutError при недочіканні й наслідки force.

    Можливі питання

    Пояснення

    «Поясни» працює з власним API-ключем Anthropic: запит іде з вашого браузера прямо до Anthropic.

    Свій API-ключ (BYOK)

    Вставте власний ключ Anthropic — пояснення працюватиме на реальній моделі. Ключ зберігається лише у вашому браузері (localStorage), нікуди не надсилається, крім api.anthropic.com, і не логується.