Strona główna / Artykuły

Playwright i TypeScript bez znajomości TypeScript: inżynier, warstwa AI i cztery etapy przetwarzania.

Inżynier QA, który pracował z Selenium i C#, tworzy framework Playwright w TypeScript z pomocą AI: struktura, zasady architektury, hooki oraz cztery bramy ticketowe.

Wydawca: REACTAPP.TOP25 min czytania5663 słów

Źródła: I Built a Playwright Framework in TypeScript Without Knowing TypeScript — Using AI

Repozytorium, które można sklonować, zawierające funkcje Skills i Rules oraz Playwright napędzany przez MCP, który samodzielnie przegląda aplikację, łatwo obiecywać, ale trudno jest je dostarczyć. Ten projekt już istnieje jako gotowy projekt: sklonuj go, wyślij jeden polecenie i testy zostaną przeprowadzone na żywej stronie. Historia jego powstania zaczyna się od starszego zestawu narzędzi oraz pytania dotyczącego języków programowania.

Migracja, która nigdy nie została zatwierdzona

Zespół QA spędził cztery lata na rozwijaniu starszego zestawu narzędzi Selenium, a niektórzy koleżani, którzy później przenieśli się do działu rozwoju, znali ten zestaw jeszcze dłużej. Jego istotą były tysiące testów interfejsu użytkownika napisanych w języku C#, zgromadzonych na przestrzeni lat przez autorów, którzy w większości opuścili firmę. Przejście na Playwright od dawna znajdowało się na liście życzeń – jest szybszy, bardziej stabilny i wygodniejszy w użyciu, ale mimo to ta migracja nigdy nie doszła do skutku.

Powód był błahy. Nikt nie miał stu dodatkowych godzin inżynierskich do poświęcenia na przepisanie kodu, którego wartość biznesowa jest zerowa, a menedżer nie może zatwierdzić decyzji opartej wyłącznie na stwierdzeniu „nowe narzędzie działa lepiej”. Zawsze istnieją argumenty techniczne, jednak im większy jest pakiet narzędzi, tym droższa staje się migracja, a korzyści wydają się tym mniejsze. Przepisanie kodu musi zostać sfinansowane z góry i w całości. Jego zwrot inwestycji nigdy nie pojawia się jako liczba w raporcie – objawia się w postaci mniejszej liczby nieudanych testów oraz szybszych napraw w ciągu kilku lat. Dlatego pomysł pozostał nierozwinięty. Playwright i TypeScript pojawiały się coraz częściej w ogłoszeniach o pracę, a brak działań stawał się coraz droższy, zarówno dla projektu, jak i dla osoby odpowiedzialnej za niego.

Następnie inna grupa rozpoczęła coś nowego, od zera: bez dziedzictwa, bez muru testów, które nie mogą zawieść, i bez konieczności uzasadniania migracji. Oferta stworzenia automatyzacji interfejsu użytkownika i API spowodowała, że w końcu Playwright i TypeScript trafiły do codziennej pracy. Ujawniło to również nieprzyjemny fakt. Java i C# były miłe w użyciu, a za nimi stało dziesięć lat kodu testowego, ale to były testy, a nie rozwój, co oznacza zupełnie inną głębię zaangażowania. JavaScript, TypeScript oraz sam Playwright były nowością. Pojawiło się pytanie, które dwa lata temu brzmiałoby retorycznie: jak bardzo brak znajomości języka może kogoś powstrzymać w 2026 roku, gdy istnieje sztuczna inteligencja?

Pochodzenie eksperymentu

Żaden pojedynczy dzień nie spowodował tej zmiany. Kształtowała się ona stopniowo, gdy stało się jasne, że same testowanie i pisanie testów do niczego nie prowadzą. QA zarabia na życie nie poprzez liczbę błędów czy liczba linii kodu, lecz poprzez kształtowanie jakości produktu za pomocą procesów, dokumentacji, automatyzacji i komunikacji, a na przestrzeni lat podejście systematyczne okazało się skuteczniejsze od indywidualnych heroicznych działań.

Dwie rzeczy napierały w tym samym kierunku.

Jeden z prób pochodził od bota do handlu kryptowalutami napisanego w Pythonie za pomocą Claude Code przez osobę, która nie znała Pythona. Proces rozpoczął się od ekscytacji związanej z pierwszym sukcesem, ale zakończył się katastrofą, gdy kod stał się zbyt skomplikowany, by ktokolwiek mógł go zrozumieć, a nikt nie potrafił wyjaśnić, dlaczego testy zawodziły ani jak je naprawić. Lekcją była różnica pomiędzy tworzeniem kodu a budową systemu: bot funkcjonował, ale nie dało się go utrzymywać. Nie istniała żadna mapa projektu, naprawy powodowały pojawianie się nowych problemów, a tokeny znikały w ciągłym opisywaniu objawów zamiast szukania przyczyn.

Kolejny impuls pochodził z codziennej pracy, gdzie sztuczna inteligencja wkradała się do rutyn sprawdzania jakości. Eksperymenty obejmowały proces od zadania po zakończone testy, ustalanie reguł, aby model je ściśle przestrzegał, oraz zapewnienie, by cały proces był powtarzalny i kontrolowalny. Koledzy używali sztucznej inteligencji jako inteligentnego pola wyszukiwania lub maszyny do wytwarzania kodu. Prawie nikt nie opracował systemu, który sprawiłby, że jej działanie byłoby przewidywalne, co wydawało się marnowaniem szansy, ponieważ to właśnie element systematyczny stanowi jego wartość.

Te dwa kierunki rozwoju doprowadziły do powstania projektu Playwright, sformułowanego jako hipoteza: jak daleko może zajść inżynier o solidnych podstawach, gdy sztuczna inteligencja wykonuje zadania pisarskie, i czy możliwe jest stworzenie kompletnego frameworku testowego bez pisania ani jednej linijki w języku, którego inżynier nie potrafi płynnie czytać?

Dlaczego TypeScript, a nie C# czy Java

Pierwsza weryfikacja była oczywista. Playwright istnieje również dla C# i Java, więc być może zmiana języka nie była konieczna. Okazało się, że różnica nie leży w sterowaniu przeglądarką – ta część jest identyczna wszędzie, z tą samą API, tymi samymi selektorami i tymi samymi przeglądarkami. Różnica polega na sposobie organizacji i wykonywania testów.

W JavaScript i TypeScript Playwright dostarcza wbudowanego narzędzia do wykonywania testów, które umożliwia równoległe wykonywanie testów, użycie fixitów, generowanie raportów oraz automatyczne śledzenie przebiegu testów. W przypadku C# i Java takiego narzędzia nie ma. Zespoły korzystają z tego, co już istnieje w danym ekosystemie – czy to xUnit, NUnit, JUnit czy TestNG – a Playwright jedynie dostarcza dla nich klas podstawowych, więc organizacja i równoległość muszą być realizowane ręcznie. To właśnie stanowi prawdziwy ograniczenie: nie dziwna składnia ani brakująca metoda, lecz konieczność ponownego tworzenia wszystkich elementów, w tym mechanizmów równoległości, izolacji danych na poziomie wątków, obsługi logów i generowania raportów – problemów, które zostały już raz rozwiązane i najlepiej unikać. Obok tego istniała opcja dostępna w pełni przygotowana, w języku, którego nikt nie znał. Playwright rozwinął się wraz z JavaScript, więc jest to narzędzie natywne, a nie dodatek.

Co naprawdę oznacza „stworzenie bez wiedzy”

Nazywanie tego projektu stworzonym bez żadnej wiedzy byłoby nie tylko błędne – to byłoby czyste kłamstwo. Stoją za nim dziesięć lat doświadczenia w obszarze QA: budowa procesów od zera oraz testowanie systemów wszelkiego rodzaju, od aplikacji internetowych i API po aplikacje mobilne. To doświadczenie zapobiegło temu, by praca utonęła w morzu wygenerowanego kodu. Składnia TypeScript i Playwright przez cały czas okazywała się niespodzianką.

Kilka kursów okazało się pomocne. Zarówno wcześniejsze eksperymenty, jak i to projekty, opierały się na kilku kursach Anthropic dotyczących projektowania agentów, łączenia umiejętności oraz formułowania zadań dla sztucznej inteligencji. Uzupełniały one doświadczenie, ale nie zastępowały go. Kluczowa była postawa. Projekt nie powstał z inicjatywy programisty szukającego prostych testów – powstał dzięki inżynierowi, który podał modelowi zasady, ograniczenia architektoniczne oraz to, co należy sprawdzić, a następnie pozwolił mu działać w tych ramach, co przekształciło chaotyczną wymianę informacji w uporządkowany proces.

Szkielet, z dwoma zastrzeżeniami

Przed przedstawieniem struktury i jej decyzji należy podkreślić dwa punkty.

Najpierw chodzi o szkielet projektu, a nie o gotowy produkt, który należy przejąć, dostosować i zmodyfikować. Jest on udostępniany po to, by pokazać podejście, a nie by twierdzić o doskonałości. Osoba szukająca gotowego narzędzia będzie rozczarowana, natomiast ta, która potrzebuje modelu do budowy systemu opartego na AI, może go znaleźć. Zespoły mają swoje własne ograniczenia, wymagania bezpieczeństwa i integracje, a projekt nadający się do demonstracji publicznej może wymagać poważnych modyfikacji w kontekście korporacyjnych rozwiązań typu SSO, MFA, specjalnych zasad dotyczących danych testowych oraz systemu zarządzania testami. Dlatego logika zawarta w kodzie stanowi tę część, która jest przenośna.

Po drugie, zestaw narzędzi był minimalny. Składał się z Claude Code w podstawowej miesięcznej subskrypcji za dwadzieścia dolarów, Visual Studio Code jako edytora, skonfigurowanego serwera MCP dla Playwright, który pozwala modelowi samodzielnie badać aplikację, oraz, co prawdopodobnie najważniejsze, świeżej głowy i trochę wolnego czasu. Nie było potrzeby drogich rozwiązań korporacyjnych, skomplikowanej infrastruktury ani rozproszonych narzędzi do testowania. Wszystko działa na zwykłym laptopie, co jest kluczowe: aby zacząć, nie potrzeba niczego nadprzyrodzonego, tylko zrozumienia tego, co się buduje, oraz chęci, by to zrobić.

Co obejmuje projekt i jak jest zorganizowany

Celem jest Toolshop – publiczny sklep demonstracyjny online stworzony do ćwiczenia z automatyzacji. Wybór jest istotny: nie ma zamkniętego systemu ani kontekstu biznesowego, więc każdy może sklonować repozytorium, zainstalować zależności za pomocą jednego polecenia i uruchomić testy na żywym serwisie, bez konieczności tworzenia kont ani przeglądania dokumentacji.

W zestawie znajduje się trzydzieści siedem testów – trzydzieści cztery z nich przeszły, a trzy zostały celowo nieudane, aby raport pokazał, jak wygląda niepowodzenie. Testy API obejmują logikę serwera, testy interfejsu – sam interfejs, a testy hybrydowe tworzą stan poprzez API, zanim sprawdzą go w przeglądarce. To wystarcza, by zademonstrować architekturę, ale za mało, by cokolwiek powiedzieć o zakresie pokrycia produktu, który zależy od konkretnego produktu i jego ryzyk biznesowych, a nie od liczby testów w demonstracji. Struktura katalogów:

config/
 env.ts // The only place where environment variables are read
 timeouts.ts // Timeouts, also in one place
core/
 run-user.ts // The user registered by this specific run
 global-setup.ts // Register → UI login → data snapshot → download OpenAPI spec
 global-teardown.ts // Cleanup after the run: remove favorites and the user
 allure-labels.ts // Parses [Section / Subsection] from the test name into report structure
 allure-categories.ts // Buckets for triaging failures in the report
api/
 generated/schema.d.ts // Types generated from OpenAPI, never touched by hand
 types.ts // Domain types derived from the schema
 client.ts // openapi-fetch, paths and params checked by the compiler
 contract.ts // Runtime validation of API responses against the schema
 auth.api.ts // Typed clients on top of api/client.ts
pages/
 base.page.ts // Base page class
 components/ // Elements present on every page (site header)
 auth/ // Page Objects for authorization pages
 account/ // Account area: account, profile, favorites
 shop/ // Catalog, product page, checkout
fixtures/
 base.fixture.ts // All dependency injection, test/expect only imported from here
data/ // Strings and routes, so they don’t live inside tests
utils/ // Logger, reference data snapshot
tests/
 auth/ // Everything auth-related in one folder
   auth.api.spec.ts // API: login, token lifetime
   registration.api.spec.ts
   login.ui.spec.ts // UI: login form, errors, logout
 catalog/
   products.api.spec.ts
   catalog.ui.spec.ts
 cart/
   cart.ui.spec.ts
 favorites/
   favorites.api.spec.ts
   favorites.hybrid.spec.ts // Hybrid: state via API, verification via UI
 profile/
   profile.hybrid.spec.ts
 contract/
   contract.api.spec.ts // Matching live API responses against the spec
 demo/ // Tests tagged @demo that fail on purpose
docs/ // Feature maps, checklists, test cases, plans
.claude/ // AI layer: rules, skills, agents, hooks```

Decyzje strukturalne, które pozwalają na skalowanie

  • Najpierw funkcjonalności, potem warstwy. Pierwotnie ta decyzja wydaje się dziwna, ale określa, jak dobrze wszystko będzie się skalować: testy są grupowane według funkcjonalności, a warstwa to sufiks nazwy pliku, jak w login.ui.spec.ts, auth.api.spec.ts i checkout.hybrid.spec.ts. Programista zmieniający logikę autoryzacji znajduje wszystkie powiązane testy pod katalogiem tests/auth/ i nie musi przeglądać trzech folderów. Przy trzydziestu testach nikt tego nie zauważa, a układ oparty na warstwach wygląda schludnie. Przy trzystu testach tests/ui/ zamienia się w bałagan z sześćdziesięcioma plikami, ponieważ zmiana w jednej funkcjonalności wpływa na wszystkie jej testy, niezależnie od warstwy. Rozproszone testy zamieniają analizę w poszukiwania.
  • Gdy nawet foldery z funkcjami stają się przepełnione. Przy tysiącach testów folder z funkcjami może zawierać setki plików. Kolejnym krokiem jest tworzenie podfolderów według poszczególnych funkcji lub scenariuszy, takich jak tests/auth/login/, tests/auth/registration/ i tests/auth/password-reset/, co rozwija tę samą zasadę przechowywania kontekstu obok kodu. Również setki testów nie powinny być umieszczane w jednym pliku, ponieważ sprawia to, że plik staje się nieczytelny i trudny do sprawdzenia.
  • Hybryda jako punkt wyjścia. Dema dzieli się na testy API, interfejsu użytkownika oraz hybrydowe, ale rzeczywisty projekt nie musi to odzwierciedlać ani tworzyć trzech warstw dla każdej funkcji. Może nie istnieć odrębne API warte przetestowania, albo testy interfejsu użytkownika mogą już być hybrydowe, przy czym warunki wstępne są ustawiane za pośrednictwem API lub bazy danych, a sprawdzana jest tylko ta część związana z interfejsem w przeglądarce. Jest to uzasadnione, ponieważ separacja dla samej separacji jest błędem. Pisanie hybrydowych testów interfejsu użytkownika od samego początku pozwala uniknąć czystych testów interfejsu, które przenoszą stan przez przeglądarkę i marnują minuty. Struktura jest środkiem, a każda dodatkowa abstrakcja zwiększa konieczność konserwacji.

Cztery decyzje, które wyjaśniają resztę

W repozytorium zapisano czternaście decyzji architektonicznych w pliku .claude/rules/architecture.md, a każda z nich odpowiada na pytanie „dlaczego w taki sposób”, a nie „co to jest”. Cztery z nich są tu ponownie przedstawione, ponieważ pozostałe lepiej pozostać w repozytorium niż być parafrazowane.

Każdy uruchomienie rejestruje swojego użytkownika

Zwykłą konfiguracją jest jeden konto testowe, które jest współdzielone przez wszystkie testy. Taka sytuacja trwa, dopóki zestaw testów się nie powiększa i testy nie są wykonywane równolegle. Wtedy jeden test opróżnia koszyk dokładnie w momencie, gdy inny sprawdza, czy produkt znajduje się w nim, co powoduje błąd, który występuje raz na dwadzieścia uruchomień i nigdy na samym komputerze debuggera.

Zasada brzmi prosto: każda sesja ma swój własny sandbox. To różni się od obietnicy unikania wspólnych danych lub ich późniejszego uporządkowania. Oznacza to, że fizycznie nie ma nikogo, z kim można by się zderzyć. W publicznej demonstracji zadanie to wykonuje nowy użytkownik zarejestrowany w global-setup. Środowisko korporacyjne może to zablokować poprzez ograniczenia dotyczące użytkowników, wspólne zasoby lub już istniejące zadania odnowy danych. Zasada pozostaje nienaruszona, a metoda tworzenia sandboxu to odrębna kwestia.

To samo rozumowanie prowadzi do wartości retries: 0. Ponawianie próby działa jak tłumik, a nie jako zabezpieczenie: przedstawia rzeczywistą sytuację konfliktową jako poprawną i odkłada konsekwencje na moment, gdy będą one najbardziej dotkliwe. Test, który wymaga ponownej próby, nie jest niestabilny, lecz zgłasza błąd izolacji, a taki raport zasługuje na rozpatrzenie. Koszt jest jasno określony: w demonstracji rejestracja użytkownika wymaga jednej prośby i zawsze kończy się sukcesem, natomiast gdzie indziej mechanizm będzie inny.

Typy pochodzą ze specyfikacji, a test kontraktowy sprawdza tę specyfikację

Są to dwa różne rodzaje sprawdzeń, które są ciągle mylone.

  • Pierwszy odbywa się w czasie kompilacji. API posiada dokument OpenAPI, z którego generowane są typy, więc pole odwołane w kodzie, ale nieobecne w specyfikacji, powoduje przerwanie kompilacji projektu. Trwa to dwa sekundy i nie wymaga żadnej prośby do serwera.
  • Drugi test sprawdza, czy serwer rzeczywiście odpowiada swoim własnym specyfikacjom. Typ wygenerowany nie może tego udowodnić, ponieważ potwierdza jedynie zgodność z dokumentem. Testy kontraktu łączą się z rzeczywistym API i porównują rzeczywiste odpowiedzi ze schematem. Już przy pierwszym uruchomieniu stwierdzono niezgodność ścieżki: w dokumencie podano products/{productId}, podczas gdy w kodzie użyto products/{id}. Innym razem okazało się, że filtr katalogu wywołuje funkcję by_category_slug, parametru w ogóle nieobecnego w specyfikacji. Serwer go akceptuje, a testy dają pozytywny wynik, więc wszystko „działa”. Jednak parametr spoza kontraktu nie może odbiegać od niego – może jedynie po cichu zniknąć pewnego dnia.

Test kontraktu nie jest zatem sprawdzeniem, czy dany punkt końcowy funkcjonuje. Sprawdza on, czy to rzeczywiście ten punkt końcowy, co do którego wszyscy się zgodzili.

Token, który istnieje przez trzysta sekund

W oczywistym projekcie token jest zapisywany raz przed rozpoczęciem działania, przechowywany w pliku i dostępny dla wszystkich procesorów. Prawie tak się stało. Jednak token w tym API istnieje dokładnie trzysta sekund, co sprawdza dedykowany test porównując dwa pola wewnątrz tokena, a sama seria testów trwa dłużej. Najwolniejszy test zakończyłby się tokenem wygasłym i błędem 401, który wygląda jak problem z uprawnieniami, a nie wygaśnięcie tokena, więc debugowanie rozpoczęłoby się w niewłaściwym miejscu.

Dlatego plik przechowuje dane uwierzytelniające, a nie tokeny. Każdy procesor ma swój własny token w pamięci i odnawia go wcześnie. Wynika z tego zasada: zawsze pobieraj token dokładnie tam, gdzie jest używany, i nigdy nie przechowuj go w zmiennej na początku bloku. Taka jedna linia kodu przywraca ten sam problem, który projekt miał na celu usunięcie.

Dane referencyjne zrzutu stanu tylko raz, a stan zmienny nigdy

Kategorie, marki oraz strona produktu nie zmieniają się pomiędzy kolejnymi uruchomieniami. Można je pobrać raz przed rozpoczęciem i przekazać testom, dzięki czemu trzydzieści testów nie żąda każdy tej samej listy. Ulubione produkty, koszyk zakupów oraz profil nigdy nie powinny znaleźć się w takim zrzucie stanu, ponieważ same testy je modyfikują. Tworzenie zrzutów stanu zmiennego nie jest optymalizacją – daje to dane przestarzałe o określonym opóźnieniu: test odczytuje wartości sprzed pięciu sekund, ponosi porażkę, a ktoś szuka błędu w aplikacji, która go nie ma.

Czyszczenie również opiera się na tej samej logice. Odbywa się w określonej kolejności – najpierw entity, a potem ich właściciele – i nigdy nie kończy się niepowodzeniem; krótkie uprawnienia powodują jedynie ostrzeżenie. Nieudane zakończenie procesu zamienia pojedynczy nieudany test w nieudane uruchomienie z błędnym wyjaśnieniem, co stanowi najgorszy rodzaj niepowodzenia.

Szczegóły dotyczące warstwy AI

Rozważmy, co się dzieje, gdy Claude Code otwiera się w folderze projektu i otrzymuje prośbę „napisz testy dla koszyka”. Model ma tę prośbę i nic więcej. Nie wie, że konta współdzielone są zabronione, że lokalizatory muszą pochodzić z zweryfikowanej mapy, ani że używanie globalnego licznika w zestawie testów równoległych to pułapka. Domyśla się, i to dość dobrze, więc kod wygląda przyzwoicie i prawdopodobnie działa. Większość ludzi na tym poprzestaje i nazywa to pracą z AI.

  • CLAUDE.md, zawsze ładowany. Plik znajdujący się w korzeniu projektu, który jest ładowany na początku każdej sesji. Służy jako wprowadzenie dla nowego współpracownika, w tym przypadku modelu, przedstawionego na dwóch ekranach: co to jest projekt, gdzie znajdują się poszczególne elementy, pięć najważniejszych punktów oraz co jest całkowicie zabronione. Słowo „zawsze” określa budżet, ponieważ każda rozmowa opłaca ładowanie tego pliku w danym kontekście. Zawiera on tylko to, co jest potrzebne za każdym razem. Czterdzieści zasad stylu znajduje się w oddzielnym pliku, do którego jest odwoływany.
  • zasady/, powody, a nie polecenia. Te pliki zawierają decyzje architektoniczne, konwencje kodowania oraz procedury przeglądarek. Polecenie mówi, aby nie przenosić tokena do zmiennej. Zasada wyjaśnia, że token istnieje przez trzysta sekund, więc ten przeniesiony na górę ginie w trakcie działania i powoduje błąd 401 w najwolniejszym teście. Mając powód, model nie potrzebuje konkretnej zakazywania – rozumie kategorię problemu i rozpoznaje ją w nieznanych warunkach.
  • umiejętności/, procedury z kontrolą. Umiejętność to instrukcja obsługi jednego zadania: kroki, opis, frazy wyzwalające, określony model oraz lista dozwolonych narzędzi. Lista narzędzi stanowi rzeczywisty limit, a nie dokumentację. Umiejętność tworząca przypadki testowe nie może wykonywać poleceń, więc nie może rozpocząć testów na wypadek, bez względu na to, jak bardzo jest chętna.
  • agenty – odrębne jednostki o ograniczonych uprawnieniach. Istnieją dwa praktyczne powody ich stosowania. Pierwszym jest kontekst: eksport strony ze wszystkimi atrybutami składa się z tysięcy niepotrzebnych elementów, a jeśli trafi do głównej rozmowy, formatowanie zastępuje rzeczywistą dyskusję. Agent działa w swoim własnym kontekście i zwraca wynik, a nie surowy materiał. Drugim powodem są granice. Agent badawczy nie ma dostępu do kodu testowego, więc przeprowadzane badania dostarczają jedynie dokumentację, a nie kod jako fakt rzeczywisty. Agent odpowiedzialny za pisanie testów może nie uruchamiać całego zestawu testów, więc pisanie bez ich wykonywania jest wystarczające.
  • haki/, które są wymuszane, ale nie są pamiętane. Zasada sformułowana w prozie obowiązuje tak długo, jak długo ktoś ją pamięta, dlatego najdroższe banowanie przekształca się w haki: małe skrypty, które uruchamiają się przed daną akcją i mogą ją zablokować. Typy wygenerowane oraz pliki .env nie podlegają edycji. Polecenia git push --force, --no-verify i --amend są automatycznie odrzucane. Hak nie przekonuje ani nie wyjaśnia nic po fakcie. Akcja nie ma miejsca, a odpowiedź wskazuje powód oraz właściwą ścieżkę.

Każda z tych koncepcji w jednym zdaniu: CLAUDE.md to materiał do przeczytania od pierwszego dnia dla nowicjusza, zasady to standardy zespołu, umiejętności to przewodniki procesowe, agenci to stażyści o ograniczonym dostępie, a haki to bramy, które pozostają zamknięte.

To samo na żywym ticketu

Abstrakcje mają sens tylko wtedy, gdy przez nie przechodzi rzeczywisty zadanie. Weźmy PROJ-431, „opłacenie zamówienia kartą”. Zwykła kolejność działań nie ma nic wspólnego z sztuczną inteligencją: ktoś testuje tę funkcję ręcznie, ktoś tworzy listę kontrolną, ktoś przekształca ją w konkretne przypadki, a ktoś automatyzuje te procesy. Jedna osoba może wykonać wszystkie cztery zadania, albo trzy osoby mogą je podzielić między siebie. Dla procesu nie ma znaczenia, kto to robi – o to właśnie chodzi, ponieważ proces ten jest nakładany na istniejący schemat działania, a nie zastępuje go. Numer zadania jest elementem łączącym wszystko, preferowanym względem nazw funkcji czy gałęzi, ponieważ przetrwa zmiany nazw i przyporządkowania.

Pierwszy krok: lista kontrolna

Inżynier QA uruchamia umiejętność i przekazuje jej numer. Dzięki połączeniu MCP z Jira umiejętność odczytuje bezpośrednio ticket, obejmując opis, kryteria przyjęcia oraz, co często jest najbardziej przydatne, komentarze, w których programista wyjaśnił połowę zachowania bez aktualizacji opisu. Gdy brakuje MCP lub ticket jest pusty, inżynier opisuje funkcję w instrukcji. Proces wymaga danych wejściowych i nie obchodzi go ich źródło.

Następnie rozpoczyna się etap, dla którego wszystko zostało stworzone. Umiejętność ta nie tworzy listy kontrolnej na podstawie tekstu zgłoszenia – zamiast tego wywołuje agenta badawczego, który wchodzi do aplikacji, zarówno w środowisku lokalnym, jak i testowym, odwiedza strony funkcji, klikaje w elementy, czyta aktualny DOM, samodzielnie wywołuje API i porównuje otrzymane odpowiedzi ze specyfikacją. Dopiero wtedy sporządzana jest lista kontrolna, oparta na tym, co faktycznie istnieje, a nie na tym, co obiecywało zgłoszenie. Kryteria przyjęcia to dane wejściowe, a nie prawda – połowa ich niezgodności z rzeczywistością stanowi pierwsze błędy zadania, które zostają znalezione jeszcze przed stworzeniem jakiegokolwiek kodu.

Powstają dwa pliki, a kluczową lekcją jest trzymanie ich oddzielnie. Pierwszy to lista kontrolna: uporządkowane według priorytetów scenariusze biznesowe w formacie zespołu, a nie sprawdzanie poszczególnych elementów. Czerwona krawędź w polu nie oznacza scenariusza; natomiast sytuacja, gdy użytkownikowi blokuje się dostęp przy użyciu czyjegoś innego karty i podaje się powód, jest scenariuszem. Drugim plikiem jest mapa funkcji, zawierająca zweryfikowane lokalizatory, pola API, reguły walidacji oraz problemy napotkane w trakcie pracy. Lista kontrolna to bezpośredni rezultat zadania, natomiast mapa służy jako podstawa do dalszych kroków. Inżynier QA czyta listę kontrolną, oznacza to, co nie jest konieczne, dodaje informacje znane tylko konkretnej osobie, a ta weryfikacja stanowi bramkę kontrolną – nic nie może przejść dalej bez zatwierdzenia.

Krok drugi: przypadki testowe

Ktokolwiek wykonał pierwszy krok może kontynuować, albo inna osoba, jeśli testy ręczne i automatyzacja są rozdzielone. Druga umiejętność rozpoczyna się od tego samego numeru zadania i sama znajduje listę kontrolną oraz mapę. Inżynier decyduje, co ma zostać objęte – całość czy tylko to, co zostało uzgodnione. Ta umiejętność przekształca te pozycje w przypadki i rozstrzyga w każdym przypadku jedną kwestię: do jakiej warstwy należy dana kontrola.

  • Zasady egzekwowane przez serwer stają się testami API.
  • Elementy widoczne dla użytkownika stają się testami interfejsu użytkownika.
  • Kontrola wymagająca piętnastu kliknięć podczas konfiguracji dla jednego stwierdzenia staje się testem hybrydowym, przy którym stan jest budowany za pomocą API, a tylko widoczna część jest sprawdzana gołym okiem.

Błędy w tym miejscu są droższe niż słabo napisane przypadki, ponieważ powodują powolne, nakładające się pokrycia, których nikt później nie odważy się usunąć. Umiejętność ta dodaje również sekcję z informacjami o usłudze do każdego przypadku: dane do użycia, lokalizatory, co musi być unikalne, co zostanie wyczyszczone oraz pod jakim kontem. Dla człowieka wygląda to jak papierkowa robota. Dla kolejnego etapu jest to gotowy opis. Inżynier sprawdza i zatwierdza go, co stanowi drugą bramkę w procesie. Zespoły w Qase lub TestRail mogą dodać tutaj umiejętność, która za pomocą API przesyła zatwierdzone przypadki i później oznacza je jako zautomatyzowane.

Trzeci krok: automatyzacja

Umiejętność automatyzacji pobiera również numer zadania oraz listę kontrolną, przypadki i sam mapę. Jej pierwszym działaniem jest coś nieoczekiwanego: sprawdza, na jak nowy jest mapa. Każda sekcja ma datę weryfikacji, a wszystko, co jest starsze niż miesiąc, przestaje być uważane za fakt i staje się twierdzeniem. Umiejętność ani nie odrzuca, ani nie ufa ślepo. Przenosi przestarzałe elementy do sekcji związanej z ryzykiem w planie i przekazuje decyzję człowiekowi – czy przeprowadzić ponowną rozpoznanie, czy zaakceptować ryzyko. Przestarzały lokalizator i tak się pojawi, ale dopiero po trzech krokach jako przekroczenie czasowe, którego przyczyna jest ukryta.

Następnie przychodzi plan: które pliki się pojawią, jakie dane wykorzystuje każdy test, co zostanie stworzone przed jego uruchomieniem i usunięte po nim, które metody strony i klienta zostaną dodane oraz jakie pytania pozostaną bez odpowiedzi. Plan stanowi trzecią i najważniejszą przeszkodę, ponieważ decyduje o tym, czy partię zadań można bezpiecznie wykonywać równolegle, a jego późniejsza zmiana jest kosztowna.

Gdy plan zostanie zatwierdzony, trafia on do agenta odpowiedzialnego za pisanie kodu, którego charakterystyczną cechą jest to, że nie wymyśla lokalizatorów. Jeśli na mapie brakuje potrzebnego lokalizatora, nie zgaduje on podobnego ani nie ukrywa tej luki, pomijając test. Zostawia on w kodzie notatkę, że nikt nie sprawdził lokalizatora, i zgłasza to problem. Test jest nadal pisany i zawodzi, co jest właściwe, ponieważ problem pozostaje widoczny i nie znika.

To, co kiedyś stanowiło oddzielne polecenia, teraz znajduje się w ramach tego kroku: uruchomienie, odczyt błędu, naprawa. Każdy test ma dwa próby, po czym agent przestaje i przyznaje porażkę. Jeden sekwencyjny wykonywanie sprawdza logikę, a dwa równoległe – izolację. Nie potrzeba dodatkowego przycisku, ponieważ nic z tego nie wymaga decyzji człowieka. To procedura określona w regułach.

Zabronienia tutaj są wyraźne i nie podlegają dyskusji:

  • Nie wyłączaj testu, który nie przeszedł, aby uzyskać wynik pozytywny.
  • Nie zwiększaj liczby prób.
  • Nie osłabiaj potajemnie żadnej kontroli, aby dostosować ją do zaobserwowanego zachowania.

Jeśli aplikacja zachowuje się niewłaściwie, prawidłowym wynikiem jest czerwony test.

Czwarty krok: przegląd i zatwierdzenie

Przegląd i zapisanie zmian to dwie odrębne umiejętności, a nie jedna – jest to celowe rozdzielenie, ponieważ krok rozpoczynający się z zamiarem zapisania zmian prowadzi gorzej do znajdowania powodów, by tego nie robić. Przegląd zawiera wiedzę specyficzną dla danego projektu, której nie posiada zwykły recenzent: co oznacza tutaj wspólna jednostka, co naraża na ryzyko token na początku bloku, dlaczego poleganie na globalnym liczniku wprowadza w błąd. Każdy wpis na tej liście pochodzi z wcześniejszego incydentu związанego z tym zestawem procedur. Aby uniknąć sytuacji, gdy to rozdzielenie staje się jedynie formalnością, przegląd umieszcza oznaczenie różnic w kodzie, które przeanalizował, a zapisanie zmian odmawia dalszej pracy, jeśli te różnice uległy zmianie. To nie jest zamek. To pytanie z dokładną odpowiedzią: czy ten kod został przeanalizowany, czy inny kod? Różnica między procesem a nawykiem leży właśnie tutaj.

Gdy cały proces zostanie zakończony, PROJ-431 zawiera pełny opis: co zostało zbadane, co wybrano do weryfikacji, co jest obsługiwane automatycznie, co pozostaje ręczne oraz co nie zostanie zautomatyzowane i dlaczego. Powodem nie jest miłość do dokumentacji. Sześć miesięcy później pytanie o to, dlaczego dany scenariusz nie został objęty obsługą, pojawia się od osoby obecnej na retrospektywie, więc musi istnieć na nie odpowiedź.

Co przetrwa zadanie

Głównym ryzykiem w takim podejściu nie jest zły kod wygenerowany automatycznie. Jest nim degradacja kontekstu po sześciu miesiącach, gdy nikt nie wie, któremu plikowi można zaufać. Dlatego artefakty mają trzy odrębne okresy życia, a ich mieszanie jest zabronione.

  • Reguły obowiązują tak długo, jak projekt, i zmieniają się jedynie wraz z kodem.
  • Mapa cech istnieje przez lata i należy do cech, a nie do zlecenia. Jest rozbudowywana o sekcje z datami, nowe strony oraz nowe problemy, nigdy nie jest odbudowywana dla każdego zlecenia osobno.
  • Listy kontrolne, przypadki i plany należą do jednego zlecenia i stają się częścią historii po jego zamknięciu.

Bez takiego podziału, rok później mapa lokalizatora istnieje w dziesięciu kopiach rozproszonych między numerami zadań, z których połowa jest błędna, a nie ma sposobu, by stwierdzić, która to połowa. Gdy mapa danej cechy urośnie, jak to ma miejsce w przypadku map dużych cech, rozwiązanie przypomina to stosowane w kodzie: należy ją podzielić na części wzdłuż naturalnych granic, takich jak strona, podcecha czy scenariusz, oraz utrzymywać krótki indeks wskazujący, co znajduje się gdzie. Obowiązuje również surowa zasada wprowadzania danych: do mapy mogą trafić tylko zweryfikowane fakty, czyli to, co agent faktycznie zobaczył w aplikacji w czasie rzeczywistym, wraz z datą. Domysły i ponowne opisy zadań nie mają tu miejsca. Mapa, która je przyjmuje, jest bezużyteczna w ramach sprintu, a fakty nie można później odróżnić od domysłów.

Żadna z tych metod nie jest uniwersalnym rozwiązaniem. Cztery etapy procesu, pięć komponentów warstwy AI, bramy decyzyjne oraz okresy trwania artefaktów to jedno z możliwych ułożeń. Zespoły różnią się liczbą pracowników, produktem oraz narzędziami, więc połowa szczegółów będzie realizowana inaczej. Niektóre rozwiązania będą skuteczne, inne wymagają dostosowania, a jeszcze inne się nie sprawdzą – i to jest do przyjęcia.

Główny punkt pozostaje niezmienny. Należy prawidłowo zbudować warstwę AI, aby model przestrzegał zasad zespołu w ramach jego granic, korzystając z jego artefaktów, a rutynowa praca nie obciążała zespołu: czytanie zgłoszeń, tworzenie podobnych przypadków, wybieranie warstw, poszukiwanie lokalizatorów, ponowne uruchamianie i naprawianie błędów, ponowna weryfikacja pod kątem tych samych problemów po raz setny. Inżynierowie QA nie są zastępowani, a istniejący proces nie jest anulowany. Czas po prostu przestaje być inwestowany w to, co można przekazać innym.

Czy nadal potrzebni są doświadczeni pracownicy?

Więcej niż kiedykolwiek potrzebni są starsi inżynierowie, ale w zmienionej roli. David Heinemeier Hansson zauważył, że najlepsze przyspieszenie dzięki agentom, jakie kiedykolwiek widział, pochodziło od najbardziej doświadczonych specjalistów, tych, którzy potrafili ocenić, czy wynik pracy agenta nadaje się do użycia.

Kiedyś inżynierowie byli cenieni za pisanie kodu, znajomość składni i szybkie wykonywanie zadań, co miało sens, gdy głównym produktem był kod. Dzięki sztucznej inteligencji ta wartość zmieniła się o rzęd wielkości, a nie o zaledwie kilka procent. Maszyna pisze kod, a człowiek decyduje, co napisać, dlaczego, w jakich granicach i jak sprawdzić, czy wynik nie jest fałszywy.

Wyobraźmy sobie dwóch specjalistów. Pierwszy otwiera Claude Code i prosi o test logowania. Ten zostaje napisany i przepuszczony, dzięki czemu specjalista czuje się produktywny, co jest prawdą w dzisiejszych czasach. Miesiąc później test przestaje działać, przyczyna pozostaje tajemnicą, patchy są wdrażane losowo, a tokeny się wyczerpują, aż w końcu wszystko zostaje przepisane albo test usunięty i zapomniany.

Drugi specjalista najpierw projektuje: jak izolować testy, jakie wzorce stosuje klient API, jak zarządzać tokenami oraz co dzieje się ze stanem, który zmienia się pomiędzy testami. Dopiero wtedy otrzymuje instrukcję skierowaną do modelu: napisz test logowania zgodnie z tymi zasadami.

Pierwszy zawiera test, który obecnie działa. Drugi zawiera system, który nadal funkcjonuje, ponieważ opiera się na architekturze, a nie na szczęściu. Przy małej liczbie testów różnica jest niewidoczna – pierwszy kończy dziesięć lub piętnaście testów w dziesięć minut, podczas gdy drugi potrzebuje dwóch godzin lub więcej na ich przeprowadzenie. Po stu, dwustu lub tysiącu testach oraz pięćdziesięciu do stu przeprowadzeniach procesu, różnica staje się oczywista.

Czy to oznacza, że trzeba nauczyć się języka programowania?

Tak, choć nie do pisania kodu. Potrzebny jest język, aby zrozumieć to, co wygenerowała sztuczna inteligencja, znaleźć błędy w kodzie, przejrzeć prośby o włączenie zmian oraz zdecydować, co trafi do głównej gałęzi. Składnia to narzędzie. Złożona struktura oparta wyłącznie na pamięci jest poza zasięgiem osoby testującej w nieznanej dziedzinie, natomiast architektura to znane terytorium: to, jak powinien wyglądać system, jak testy pozostają izolowane, jak są zorganizowane procesy i co powinien zawierać dobry raport. To właśnie ta podstawa umożliwiła powstanie projektu Playwright.

Architektura nie powstaje na podstawie prośby. Sztuczna inteligencja może ją zaproponować, czasem dobrze, ale architektura to wybór spośród różnych rozwiązań, a nie samo rozwiązanie. Model oferuje pięć opcji, które wszystkie wydają się rozsądne. To, która z nich sprawdzi się w ciągu sześciu miesięcy, przy dziesięciokrotnie większej liczbie testów i zmienionych wymaganiach, nie jest czymś, co można wygenerować. Pochodzi ona z doświadczenia, z umiejętności nabytej poprzez naprawianie błędnych testów, zrozumienia, dlaczego test przepada lokalnie, ale udaje się w środowisku CI, oraz odkrycia, że wspólny konto stanowi ukryty element powodujący problemy, który może w każdej chwili wybuchnąć.

Firmy nie potrzebują ludzi, którzy tylko naciskają przyciski i piszą polecenia w Claude Code lub Cursor. Takich osób jest mnóstwo i będzie ich jeszcze więcej, a rynek jest pełen programistów, którzy myślą, że znaleźli skarb, choć to uczucie trwa tylko krótko. Potrzebni są ludzie, którzy rozumieją, dlaczego sztuczna inteligencja coś zrobiła, zauważają, gdy popełnia błąd, formułują zadania tak, aby model dostarczył sprawne rozwiązanie, a nie dopracowany błąd, oraz budują system zamiast wprowadzać tokeny w nieskończone cykle „spróbuj ponownie, nie tak”.

Doświadczenie w obszarze przetwarzania danych, automatyzacji i pipeline’ów nie jest czymś przeszłym. Stanowi podstawę skutecznego pracy z sztuczną inteligencją i odróżnia osobę, która tworzy chaos, od tej, która buduje system. Wiedza kiedyś służyła do wykonywania zadań ręcznie. Teraz służy do zadawania zadań sztucznej inteligencji oraz sprawdzania wyników. Rola się zmieniła, ale wartość wiedzy specjalistycznej pozostała niezmienna. Jest to łatwiejsze do zrozumienia, ponieważ obok sztucznej inteligencji jest jasne, kto jedynie generuje dane, a kto rozumie, co zostało stworzone i dlaczego.

Co pokazuje eksperyment

Została stworzona ramowa struktura w języku, którego jej twórca nie zna, i to nie jest historia o genialności. To historia o tym, by nie próbować zgadywać, co zrobi sztuczna inteligencja, lecz poświęcić czas na wyjaśnienie zasad gry: co jest dozwolone, a co nie, oraz dlaczego w taki, a nie inny sposób. To nudne, nie ma to nic wspólnego z atmosferą i potrzeba czasu, zanim pojawi się pierwszy rezultat. To właśnie działa.

Repozytorium nie dowodzi, że inżynierowie są przestarzali lub że języki przestały mieć znaczenie. To żywy test tego, jak zmienia się rola inżyniera, gdy wartość przesuwa się z pisania kodu na formułowanie zadań, projektowanie systemów i sprawdzanie wyników. Pisanie kodu ręcznie pozostaje umiejętnością. Do niej dołączyła druga, równie ważna: zrozumienie tego, co zostało stworzone, dlaczego i co nastąpi dalej.

Sztuczna inteligencja nie zastępuje inżyniera. Staje się najszybszym i najposłuszniejszym możliwym asystentem, niezmordowanym, który nigdy się nie kłóci i zawsze jest gotowy na kolejną próbę. Ma jeden dziwactwo: z tą samą pewnością siebie dostarcza zarówno poprawne rozwiązania, jak i całkowitą bzdurę, a tylko osoba znająca temat może je odróżnić. Dlatego inżynier musi pozostać przy sterze jako ten, kto zna cel i drogę, a nie jako ktoś, kto jedynie trzyma kierownicę.